summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/static_checks.yml1
-rw-r--r--core/config/project_settings.cpp2
-rw-r--r--core/core_bind.cpp10
-rw-r--r--core/core_bind.h2
-rw-r--r--core/io/resource.cpp36
-rw-r--r--core/io/resource.h11
-rw-r--r--core/io/resource_format_binary.cpp3
-rw-r--r--core/object/message_queue.cpp94
-rw-r--r--core/object/message_queue.h2
-rw-r--r--core/object/object.h10
-rw-r--r--core/object/worker_thread_pool.h21
-rw-r--r--core/os/os.cpp4
-rw-r--r--core/os/os.h3
-rw-r--r--core/string/print_string.cpp2
-rw-r--r--core/templates/hash_map.h34
-rw-r--r--core/variant/array.cpp12
-rw-r--r--doc/classes/Array.xml1
-rw-r--r--doc/classes/CPUParticles2D.xml7
-rw-r--r--doc/classes/CPUParticles3D.xml7
-rw-r--r--doc/classes/CollisionShape3D.xml4
-rw-r--r--doc/classes/DisplayServer.xml33
-rw-r--r--doc/classes/EditorExportPlugin.xml75
-rw-r--r--doc/classes/EditorPlugin.xml34
-rw-r--r--doc/classes/EditorSettings.xml3
-rw-r--r--doc/classes/FileDialog.xml4
-rw-r--r--doc/classes/GPUParticles2D.xml9
-rw-r--r--doc/classes/GPUParticles3D.xml8
-rw-r--r--doc/classes/OS.xml17
-rw-r--r--doc/classes/ProjectSettings.xml6
-rw-r--r--doc/classes/RayCast3D.xml3
-rw-r--r--doc/classes/RenderingDevice.xml38
-rw-r--r--doc/classes/RichTextLabel.xml18
-rw-r--r--doc/classes/ShapeCast3D.xml4
-rw-r--r--doc/classes/TileMap.xml56
-rw-r--r--doc/classes/VisualShaderNode.xml7
-rw-r--r--doc/classes/VisualShaderNodeCustom.xml8
-rw-r--r--doc/classes/Window.xml3
-rwxr-xr-xdoc/tools/doc_status.py6
-rwxr-xr-xdoc/tools/make_rst.py48
-rw-r--r--drivers/png/resource_saver_png.cpp2
-rw-r--r--drivers/unix/net_socket_posix.cpp20
-rw-r--r--drivers/unix/net_socket_posix.h1
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp89
-rw-r--r--editor/animation_track_editor.cpp4
-rw-r--r--editor/create_dialog.cpp6
-rw-r--r--editor/debugger/editor_profiler.cpp1
-rw-r--r--editor/debugger/editor_profiler.h2
-rw-r--r--editor/debugger/editor_visual_profiler.cpp1
-rw-r--r--editor/debugger/editor_visual_profiler.h2
-rw-r--r--editor/editor_help.cpp49
-rw-r--r--editor/editor_inspector.cpp1
-rw-r--r--editor/editor_inspector.h1
-rw-r--r--editor/editor_log.cpp8
-rw-r--r--editor/editor_node.cpp91
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_plugin.cpp18
-rw-r--r--editor/editor_plugin.h5
-rw-r--r--editor/editor_properties.cpp4
-rw-r--r--editor/editor_resource_picker.cpp124
-rw-r--r--editor/editor_resource_picker.h19
-rw-r--r--editor/editor_resource_preview.cpp1
-rw-r--r--editor/editor_resource_preview.h4
-rw-r--r--editor/editor_run_native.cpp1
-rw-r--r--editor/editor_settings.cpp1
-rw-r--r--editor/editor_themes.cpp5
-rw-r--r--editor/editor_undo_redo_manager.cpp2
-rw-r--r--editor/export/editor_export_platform.cpp25
-rw-r--r--editor/export/editor_export_platform.h1
-rw-r--r--editor/export/editor_export_platform_pc.cpp1
-rw-r--r--editor/export/editor_export_plugin.cpp81
-rw-r--r--editor/export/editor_export_plugin.h27
-rw-r--r--editor/export/editor_export_preset.cpp21
-rw-r--r--editor/export/export_template_manager.cpp7
-rw-r--r--editor/gui/editor_toaster.cpp1
-rw-r--r--editor/gui/editor_toaster.h1
-rw-r--r--editor/import/audio_stream_import_settings.cpp4
-rw-r--r--editor/import/resource_importer_layered_texture.cpp1
-rw-r--r--editor/import/resource_importer_texture.cpp1
-rw-r--r--editor/import/resource_importer_texture_atlas.cpp4
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp1
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp1
-rw-r--r--editor/plugins/animation_player_editor_plugin.h1
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp1
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp1
-rw-r--r--editor/plugins/audio_stream_editor_plugin.cpp5
-rw-r--r--editor/plugins/bit_map_editor_plugin.cpp1
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp9
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h3
-rw-r--r--editor/plugins/curve_editor_plugin.cpp6
-rw-r--r--editor/plugins/dedicated_server_export_plugin.h2
-rw-r--r--editor/plugins/editor_preview_plugins.cpp3
-rw-r--r--editor/plugins/gdextension_export_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp1
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp1
-rw-r--r--editor/plugins/gradient_editor.cpp3
-rw-r--r--editor/plugins/gradient_editor.h2
-rw-r--r--editor/plugins/gradient_texture_2d_editor_plugin.cpp3
-rw-r--r--editor/plugins/gradient_texture_2d_editor_plugin.h1
-rw-r--r--editor/plugins/input_event_editor_plugin.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/polygon_3d_editor_plugin.cpp5
-rw-r--r--editor/plugins/script_editor_plugin.cpp59
-rw-r--r--editor/plugins/script_editor_plugin.h2
-rw-r--r--editor/plugins/script_text_editor.cpp12
-rw-r--r--editor/plugins/shader_editor_plugin.cpp21
-rw-r--r--editor/plugins/shader_editor_plugin.h1
-rw-r--r--editor/plugins/shader_file_editor_plugin.cpp4
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp1
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h1
-rw-r--r--editor/plugins/style_box_editor_plugin.cpp5
-rw-r--r--editor/plugins/text_shader_editor.cpp8
-rw-r--r--editor/plugins/texture_3d_editor_plugin.cpp6
-rw-r--r--editor/plugins/texture_editor_plugin.cpp7
-rw-r--r--editor/plugins/texture_layered_editor_plugin.cpp4
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp11
-rw-r--r--editor/plugins/texture_region_editor_plugin.h6
-rw-r--r--editor/plugins/theme_editor_plugin.cpp16
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.cpp1
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp4
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp6
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp4
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp4
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp1
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp57
-rw-r--r--editor/project_manager.cpp1
-rw-r--r--main/main.cpp3
-rw-r--r--modules/csg/csg_shape.cpp4
-rw-r--r--modules/dds/texture_loader_dds.cpp1
-rw-r--r--modules/dds/texture_loader_dds.h1
-rw-r--r--modules/gdscript/gdscript.cpp2
-rw-r--r--modules/gdscript/gdscript_cache.cpp5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp13
-rw-r--r--modules/gdscript/register_types.cpp2
-rw-r--r--modules/gltf/gltf_document.cpp62
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml4
-rw-r--r--modules/gridmap/grid_map.cpp13
-rw-r--r--modules/gridmap/grid_map.h2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs60
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs42
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs26
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs32
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.cpp4
-rw-r--r--modules/navigation/nav_region.cpp4
-rw-r--r--modules/noise/noise_texture_2d.cpp10
-rw-r--r--modules/noise/noise_texture_3d.cpp10
-rw-r--r--modules/noise/tests/test_noise_texture_2d.h8
-rw-r--r--modules/noise/tests/test_noise_texture_3d.h6
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.cpp4
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp33
-rw-r--r--modules/raycast/raycast_occlusion_cull.h10
-rw-r--r--modules/text_server_adv/text_server_adv.cpp1
-rw-r--r--modules/text_server_adv/text_server_adv.h2
-rw-r--r--modules/text_server_fb/text_server_fb.h2
-rw-r--r--modules/theora/video_stream_theora.cpp1
-rw-r--r--modules/theora/video_stream_theora.h2
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp12
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h2
-rw-r--r--modules/vorbis/doc_classes/AudioStreamOggVorbis.xml18
-rw-r--r--modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml16
-rw-r--r--modules/vorbis/register_types.cpp3
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.cpp94
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.h9
-rw-r--r--modules/webp/resource_saver_webp.cpp2
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml12
-rw-r--r--platform/android/export/export_plugin.cpp196
-rw-r--r--platform/android/export/export_plugin.h36
-rw-r--r--platform/android/export/godot_plugin_config.cpp36
-rw-r--r--platform/android/export/godot_plugin_config.h12
-rw-r--r--platform/android/export/gradle_export_util.cpp101
-rw-r--r--platform/android/export/gradle_export_util.h20
-rw-r--r--platform/android/java/app/src/com/godot/game/GodotApp.java4
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java154
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java1195
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt965
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt167
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java429
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java20
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotHost.java9
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java5
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotService.kt54
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java13
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java9
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java11
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java32
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java12
-rw-r--r--platform/android/java_godot_lib_jni.cpp2
-rw-r--r--platform/android/java_godot_wrapper.cpp76
-rw-r--r--platform/android/java_godot_wrapper.h11
-rw-r--r--platform/ios/doc_classes/EditorExportPlatformIOS.xml3
-rw-r--r--platform/ios/export/export_plugin.cpp10
-rw-r--r--platform/ios/export/export_plugin.h1
-rw-r--r--platform/linuxbsd/export/export_plugin.h2
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp2
-rw-r--r--platform/macos/display_server_macos.h2
-rw-r--r--platform/macos/display_server_macos.mm177
-rw-r--r--platform/macos/doc_classes/EditorExportPlatformMacOS.xml3
-rw-r--r--platform/macos/export/export_plugin.cpp12
-rw-r--r--platform/macos/os_macos.h4
-rw-r--r--platform/macos/os_macos.mm59
-rw-r--r--platform/uwp/export/export_plugin.cpp3
-rw-r--r--platform/uwp/export/export_plugin.h1
-rw-r--r--platform/web/display_server_web.cpp1
-rw-r--r--platform/web/export/export_plugin.cpp1
-rw-r--r--platform/windows/display_server_windows.cpp129
-rw-r--r--platform/windows/display_server_windows.h2
-rw-r--r--scene/2d/collision_shape_2d.cpp4
-rw-r--r--scene/2d/cpu_particles_2d.cpp42
-rw-r--r--scene/2d/cpu_particles_2d.h2
-rw-r--r--scene/2d/gpu_particles_2d.cpp62
-rw-r--r--scene/2d/gpu_particles_2d.h7
-rw-r--r--scene/2d/light_occluder_2d.cpp4
-rw-r--r--scene/2d/line_2d.cpp9
-rw-r--r--scene/2d/multimesh_instance_2d.cpp5
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp24
-rw-r--r--scene/2d/navigation_region_2d.cpp5
-rw-r--r--scene/2d/path_2d.cpp4
-rw-r--r--scene/2d/physics_body_2d.cpp13
-rw-r--r--scene/2d/shape_cast_2d.cpp5
-rw-r--r--scene/2d/sprite_2d.cpp5
-rw-r--r--scene/2d/tile_map.compat.inc45
-rw-r--r--scene/2d/tile_map.cpp4147
-rw-r--r--scene/2d/tile_map.h405
-rw-r--r--scene/2d/touch_screen_button.cpp4
-rw-r--r--scene/3d/camera_3d.cpp5
-rw-r--r--scene/3d/collision_object_3d.cpp14
-rw-r--r--scene/3d/collision_shape_3d.cpp17
-rw-r--r--scene/3d/collision_shape_3d.h2
-rw-r--r--scene/3d/cpu_particles_3d.cpp37
-rw-r--r--scene/3d/cpu_particles_3d.h2
-rw-r--r--scene/3d/gpu_particles_3d.cpp60
-rw-r--r--scene/3d/gpu_particles_3d.h9
-rw-r--r--scene/3d/label_3d.cpp19
-rw-r--r--scene/3d/lightmap_gi.cpp1
-rw-r--r--scene/3d/mesh_instance_3d.cpp5
-rw-r--r--scene/3d/navigation_region_3d.cpp19
-rw-r--r--scene/3d/occluder_instance_3d.cpp5
-rw-r--r--scene/3d/path_3d.cpp4
-rw-r--r--scene/3d/physics_body_3d.cpp13
-rw-r--r--scene/3d/ray_cast_3d.cpp13
-rw-r--r--scene/3d/ray_cast_3d.h4
-rw-r--r--scene/3d/shape_cast_3d.cpp21
-rw-r--r--scene/3d/shape_cast_3d.h4
-rw-r--r--scene/3d/skeleton_3d.cpp2
-rw-r--r--scene/3d/sprite_3d.cpp5
-rw-r--r--scene/animation/animation_blend_tree.cpp8
-rw-r--r--scene/gui/button.cpp14
-rw-r--r--scene/gui/color_picker.cpp3
-rw-r--r--scene/gui/color_picker.h1
-rw-r--r--scene/gui/control.cpp34
-rw-r--r--scene/gui/file_dialog.cpp43
-rw-r--r--scene/gui/file_dialog.h8
-rw-r--r--scene/gui/graph_edit.cpp1
-rw-r--r--scene/gui/label.cpp5
-rw-r--r--scene/gui/nine_patch_rect.cpp5
-rw-r--r--scene/gui/option_button.cpp12
-rw-r--r--scene/gui/popup_menu.cpp6
-rw-r--r--scene/gui/popup_menu.h2
-rw-r--r--scene/gui/rich_text_label.cpp46
-rw-r--r--scene/gui/rich_text_label.h12
-rw-r--r--scene/gui/texture_button.cpp5
-rw-r--r--scene/gui/texture_progress_bar.cpp6
-rw-r--r--scene/gui/texture_rect.cpp6
-rw-r--r--scene/gui/video_stream_player.cpp1
-rw-r--r--scene/gui/video_stream_player.h2
-rw-r--r--scene/main/node.cpp5
-rw-r--r--scene/main/scene_tree.cpp1
-rw-r--r--scene/main/viewport.cpp11
-rw-r--r--scene/main/window.cpp34
-rw-r--r--scene/main/window.h2
-rw-r--r--scene/register_scene_types.cpp13
-rw-r--r--scene/resources/animated_texture.cpp286
-rw-r--r--scene/resources/animated_texture.h109
-rw-r--r--scene/resources/animation.cpp10
-rw-r--r--scene/resources/animation_library.cpp12
-rw-r--r--scene/resources/atlas_texture.cpp255
-rw-r--r--scene/resources/atlas_texture.h79
-rw-r--r--scene/resources/box_shape_3d.cpp2
-rw-r--r--scene/resources/camera_attributes.cpp2
-rw-r--r--scene/resources/camera_texture.cpp131
-rw-r--r--scene/resources/camera_texture.h68
-rw-r--r--scene/resources/capsule_shape_3d.cpp4
-rw-r--r--scene/resources/compressed_texture.cpp907
-rw-r--r--scene/resources/compressed_texture.h273
-rw-r--r--scene/resources/concave_polygon_shape_3d.cpp4
-rw-r--r--scene/resources/convex_polygon_shape_3d.cpp2
-rw-r--r--scene/resources/curve_texture.cpp376
-rw-r--r--scene/resources/curve_texture.h122
-rw-r--r--scene/resources/cylinder_shape_3d.cpp4
-rw-r--r--scene/resources/default_theme/default_theme.cpp5
-rw-r--r--scene/resources/environment.cpp7
-rw-r--r--scene/resources/font.cpp38
-rw-r--r--scene/resources/gradient_texture.cpp438
-rw-r--r--scene/resources/gradient_texture.h144
-rw-r--r--scene/resources/height_map_shape_3d.cpp6
-rw-r--r--scene/resources/image_texture.cpp514
-rw-r--r--scene/resources/image_texture.h203
-rw-r--r--scene/resources/label_settings.cpp6
-rw-r--r--scene/resources/material.cpp4
-rw-r--r--scene/resources/mesh_library.cpp8
-rw-r--r--scene/resources/mesh_texture.cpp156
-rw-r--r--scene/resources/mesh_texture.h75
-rw-r--r--scene/resources/particle_process_material.cpp1
-rw-r--r--scene/resources/placeholder_textures.cpp177
-rw-r--r--scene/resources/placeholder_textures.h130
-rw-r--r--scene/resources/portable_compressed_texture.cpp339
-rw-r--r--scene/resources/portable_compressed_texture.h110
-rw-r--r--scene/resources/primitive_meshes.cpp13
-rw-r--r--scene/resources/resource_format_text.cpp5
-rw-r--r--scene/resources/separation_ray_shape_3d.cpp6
-rw-r--r--scene/resources/shader.cpp33
-rw-r--r--scene/resources/shader_include.cpp4
-rw-r--r--scene/resources/sphere_shape_3d.cpp2
-rw-r--r--scene/resources/style_box.cpp924
-rw-r--r--scene/resources/style_box.h188
-rw-r--r--scene/resources/style_box_flat.cpp642
-rw-r--r--scene/resources/style_box_flat.h118
-rw-r--r--scene/resources/style_box_line.cpp132
-rw-r--r--scene/resources/style_box_line.h70
-rw-r--r--scene/resources/style_box_texture.cpp243
-rw-r--r--scene/resources/style_box_texture.h99
-rw-r--r--scene/resources/texture.cpp3292
-rw-r--r--scene/resources/texture.h949
-rw-r--r--scene/resources/theme.cpp34
-rw-r--r--scene/resources/tile_set.cpp10
-rw-r--r--scene/resources/tile_set.h2
-rw-r--r--scene/resources/visual_shader.cpp19
-rw-r--r--scene/resources/visual_shader.h3
-rw-r--r--scene/resources/visual_shader_nodes.cpp10
-rw-r--r--scene/resources/visual_shader_nodes.h6
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp16
-rw-r--r--scene/resources/visual_shader_particle_nodes.h2
-rw-r--r--scene/resources/world_boundary_shape_3d.cpp2
-rw-r--r--scene/scene_string_names.cpp3
-rw-r--r--scene/scene_string_names.h3
-rw-r--r--servers/display_server.cpp13
-rw-r--r--servers/display_server.h10
-rw-r--r--servers/navigation_server_3d.cpp6
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.cpp4
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.h2
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp11
-rw-r--r--servers/rendering/rendering_device.cpp4
-rw-r--r--servers/rendering/rendering_device.h11
-rw-r--r--servers/rendering/rendering_server_default.cpp12
-rw-r--r--servers/rendering/rendering_server_default.h31
-rw-r--r--servers/rendering/shader_language.cpp24
-rw-r--r--tests/core/io/test_resource.h52
-rw-r--r--tests/core/threads/test_worker_thread_pool.h26
-rw-r--r--tests/core/variant/test_array.h26
-rw-r--r--tests/scene/test_theme.h2
-rw-r--r--tests/servers/test_navigation_server_3d.h481
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/enet/enet/enet.h16
-rw-r--r--thirdparty/enet/godot.cpp4
-rw-r--r--thirdparty/enet/host.c16
-rw-r--r--thirdparty/enet/packet.c77
-rw-r--r--thirdparty/enet/peer.c84
-rw-r--r--thirdparty/enet/protocol.c210
362 files changed, 14171 insertions, 10266 deletions
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index 6133780688..b47ef135a2 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -86,6 +86,7 @@ jobs:
- name: Documentation checks
run: |
+ doc/tools/doc_status.py doc/classes modules/*/doc_classes platform/*/doc_classes
doc/tools/make_rst.py --dry-run --color doc/classes modules platform
- name: Style checks via clang-format (clang_format.sh)
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 79fab50882..715ed61770 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1344,7 +1344,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/occlusion_culling/bvh_build_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), 2);
GLOBAL_DEF(PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1"), 60); // No negative and limit to 500 due to crashes.
GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false);
- GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_RANGE, "Based on Locale,Left-to-Right,Right-to-Left"), 0);
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Locale,Left-to-Right,Right-to-Left"), 0);
GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000);
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 2d0d24406c..a73b198be2 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -442,6 +442,10 @@ bool OS::has_feature(const String &p_feature) const {
}
}
+bool OS::is_sandboxed() const {
+ return ::OS::get_singleton()->is_sandboxed();
+}
+
uint64_t OS::get_static_memory_usage() const {
return ::OS::get_singleton()->get_static_memory_usage();
}
@@ -545,6 +549,10 @@ Vector<String> OS::get_granted_permissions() const {
return ::OS::get_singleton()->get_granted_permissions();
}
+void OS::revoke_granted_permissions() {
+ ::OS::get_singleton()->revoke_granted_permissions();
+}
+
String OS::get_unique_id() const {
return ::OS::get_singleton()->get_unique_id();
}
@@ -636,10 +644,12 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_main_thread_id"), &OS::get_main_thread_id);
ClassDB::bind_method(D_METHOD("has_feature", "tag_name"), &OS::has_feature);
+ ClassDB::bind_method(D_METHOD("is_sandboxed"), &OS::is_sandboxed);
ClassDB::bind_method(D_METHOD("request_permission", "name"), &OS::request_permission);
ClassDB::bind_method(D_METHOD("request_permissions"), &OS::request_permissions);
ClassDB::bind_method(D_METHOD("get_granted_permissions"), &OS::get_granted_permissions);
+ ClassDB::bind_method(D_METHOD("revoke_granted_permissions"), &OS::revoke_granted_permissions);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec");
diff --git a/core/core_bind.h b/core/core_bind.h
index a0d63101d2..dc0b2a1cf5 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -234,10 +234,12 @@ public:
::Thread::ID get_main_thread_id() const;
bool has_feature(const String &p_feature) const;
+ bool is_sandboxed() const;
bool request_permission(const String &p_name);
bool request_permissions();
Vector<String> get_granted_permissions() const;
+ void revoke_granted_permissions();
static OS *get_singleton() { return singleton; }
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 6b8ec8d5f6..07677337b4 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -147,15 +147,28 @@ bool Resource::editor_can_reload_from_file() {
return true; //by default yes
}
+void Resource::connect_changed(const Callable &p_callable, uint32_t p_flags) {
+ if (!is_connected(CoreStringNames::get_singleton()->changed, p_callable) || p_flags & CONNECT_REFERENCE_COUNTED) {
+ connect(CoreStringNames::get_singleton()->changed, p_callable, p_flags);
+ }
+}
+
+void Resource::disconnect_changed(const Callable &p_callable) {
+ if (is_connected(CoreStringNames::get_singleton()->changed, p_callable)) {
+ disconnect(CoreStringNames::get_singleton()->changed, p_callable);
+ }
+}
+
void Resource::reset_state() {
}
+
Error Resource::copy_from(const Ref<Resource> &p_resource) {
ERR_FAIL_COND_V(p_resource.is_null(), ERR_INVALID_PARAMETER);
if (get_class() != p_resource->get_class()) {
return ERR_INVALID_PARAMETER;
}
- reset_state(); //may want to reset state
+ reset_state(); // May want to reset state.
List<PropertyInfo> pi;
p_resource->get_property_list(&pi);
@@ -322,23 +335,6 @@ RID Resource::get_rid() const {
return RID();
}
-void Resource::register_owner(Object *p_owner) {
- owners.insert(p_owner->get_instance_id());
-}
-
-void Resource::unregister_owner(Object *p_owner) {
- owners.erase(p_owner->get_instance_id());
-}
-
-void Resource::notify_change_to_owners() {
- for (const ObjectID &E : owners) {
- Object *obj = ObjectDB::get_instance(E);
- ERR_CONTINUE_MSG(!obj, "Object was deleted, while still owning a resource."); //wtf
- //TODO store string
- obj->call("resource_changed", Ref<Resource>(this));
- }
-}
-
#ifdef TOOLS_ENABLED
uint32_t Resource::hash_edited_version() const {
@@ -443,6 +439,7 @@ void Resource::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_local_to_scene"), &Resource::is_local_to_scene);
ClassDB::bind_method(D_METHOD("get_local_scene"), &Resource::get_local_scene);
ClassDB::bind_method(D_METHOD("setup_local_to_scene"), &Resource::setup_local_to_scene);
+
ClassDB::bind_method(D_METHOD("emit_changed"), &Resource::emit_changed);
ClassDB::bind_method(D_METHOD("duplicate", "subresources"), &Resource::duplicate, DEFVAL(false));
@@ -469,9 +466,6 @@ Resource::~Resource() {
ResourceCache::resources.erase(path_cache);
ResourceCache::lock.unlock();
}
- if (owners.size()) {
- WARN_PRINT("Resource is still owned.");
- }
}
HashMap<String, Resource *> ResourceCache::resources;
diff --git a/core/io/resource.h b/core/io/resource.h
index 5135664f36..af8c275a1c 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -54,8 +54,6 @@ public:
virtual String get_base_extension() const { return "res"; }
private:
- HashSet<ObjectID> owners;
-
friend class ResBase;
friend class ResourceCache;
@@ -76,10 +74,6 @@ private:
SelfList<Resource> remapped_list;
protected:
- void emit_changed();
-
- void notify_change_to_owners();
-
virtual void _resource_path_changed();
static void _bind_methods();
@@ -96,8 +90,9 @@ public:
virtual Error copy_from(const Ref<Resource> &p_resource);
virtual void reload_from_file();
- void register_owner(Object *p_owner);
- void unregister_owner(Object *p_owner);
+ void emit_changed();
+ void connect_changed(const Callable &p_callable, uint32_t p_flags = 0);
+ void disconnect_changed(const Callable &p_callable);
void set_name(const String &p_name);
String get_name() const;
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 3037f603c4..adae468d93 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1960,6 +1960,8 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
return;
}
+ resource_set.insert(res);
+
List<PropertyInfo> property_list;
res->get_property_list(&property_list);
@@ -1983,7 +1985,6 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
}
}
- resource_set.insert(res);
saved_resources.push_back(res);
} break;
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
index 18ba5d5b30..506f8291eb 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -222,62 +222,66 @@ void CallQueue::_call_function(const Callable &p_callable, const Variant *p_args
}
}
-Error CallQueue::flush() {
- LOCK_MUTEX;
-
- // Thread overrides are not meant to be flushed, but appended to the main one.
- if (this == MessageQueue::thread_singleton) {
- if (pages.size() == 0) {
- return OK;
- }
+Error CallQueue::_transfer_messages_to_main_queue() {
+ if (pages.size() == 0) {
+ return OK;
+ }
- CallQueue *mq = MessageQueue::main_singleton;
- DEV_ASSERT(!mq->allocator_is_custom && !allocator_is_custom); // Transferring pages is only safe if using the same alloator parameters.
-
- mq->mutex.lock();
-
- // Here we're transferring the data from this queue to the main one.
- // However, it's very unlikely big amounts of messages will be queued here,
- // so PagedArray/Pool would be overkill. Also, in most cases the data will fit
- // an already existing page of the main queue.
-
- // Let's see if our first (likely only) page fits the current target queue page.
- uint32_t src_page = 0;
- {
- if (mq->pages_used) {
- uint32_t dst_page = mq->pages_used - 1;
- uint32_t dst_offset = mq->page_bytes[dst_page];
- if (dst_offset + page_bytes[0] < uint32_t(PAGE_SIZE_BYTES)) {
- memcpy(mq->pages[dst_page]->data + dst_offset, pages[0]->data, page_bytes[0]);
- mq->page_bytes[dst_page] += page_bytes[0];
- src_page++;
- }
+ CallQueue *mq = MessageQueue::main_singleton;
+ DEV_ASSERT(!mq->allocator_is_custom && !allocator_is_custom); // Transferring pages is only safe if using the same alloator parameters.
+
+ mq->mutex.lock();
+
+ // Here we're transferring the data from this queue to the main one.
+ // However, it's very unlikely big amounts of messages will be queued here,
+ // so PagedArray/Pool would be overkill. Also, in most cases the data will fit
+ // an already existing page of the main queue.
+
+ // Let's see if our first (likely only) page fits the current target queue page.
+ uint32_t src_page = 0;
+ {
+ if (mq->pages_used) {
+ uint32_t dst_page = mq->pages_used - 1;
+ uint32_t dst_offset = mq->page_bytes[dst_page];
+ if (dst_offset + page_bytes[0] < uint32_t(PAGE_SIZE_BYTES)) {
+ memcpy(mq->pages[dst_page]->data + dst_offset, pages[0]->data, page_bytes[0]);
+ mq->page_bytes[dst_page] += page_bytes[0];
+ src_page++;
}
}
+ }
- // Any other possibly existing source page needs to be added.
+ // Any other possibly existing source page needs to be added.
- if (mq->pages_used + (pages_used - src_page) > mq->max_pages) {
- ERR_PRINT("Failed appending thread queue. Message queue out of memory. " + mq->error_text);
- mq->statistics();
- mq->mutex.unlock();
- return ERR_OUT_OF_MEMORY;
- }
+ if (mq->pages_used + (pages_used - src_page) > mq->max_pages) {
+ ERR_PRINT("Failed appending thread queue. Message queue out of memory. " + mq->error_text);
+ mq->statistics();
+ mq->mutex.unlock();
+ return ERR_OUT_OF_MEMORY;
+ }
- for (; src_page < pages_used; src_page++) {
- mq->_add_page();
- memcpy(mq->pages[mq->pages_used - 1]->data, pages[src_page]->data, page_bytes[src_page]);
- mq->page_bytes[mq->pages_used - 1] = page_bytes[src_page];
- }
+ for (; src_page < pages_used; src_page++) {
+ mq->_add_page();
+ memcpy(mq->pages[mq->pages_used - 1]->data, pages[src_page]->data, page_bytes[src_page]);
+ mq->page_bytes[mq->pages_used - 1] = page_bytes[src_page];
+ }
- mq->mutex.unlock();
+ mq->mutex.unlock();
- page_bytes[0] = 0;
- pages_used = 1;
+ page_bytes[0] = 0;
+ pages_used = 1;
- return OK;
+ return OK;
+}
+
+Error CallQueue::flush() {
+ // Thread overrides are not meant to be flushed, but appended to the main one.
+ if (unlikely(this == MessageQueue::thread_singleton)) {
+ return _transfer_messages_to_main_queue();
}
+ LOCK_MUTEX;
+
if (pages.size() == 0) {
// Never allocated
UNLOCK_MUTEX;
diff --git a/core/object/message_queue.h b/core/object/message_queue.h
index 9f567e4dd0..c2f4ad1643 100644
--- a/core/object/message_queue.h
+++ b/core/object/message_queue.h
@@ -98,6 +98,8 @@ private:
}
}
+ Error _transfer_messages_to_main_queue();
+
void _add_page();
void _call_function(const Callable &p_callable, const Variant *p_args, int p_argcount, bool p_show_error);
diff --git a/core/object/object.h b/core/object/object.h
index a3e9d025ea..318dbf98de 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -430,6 +430,9 @@ protected:
_FORCE_INLINE_ static void (*_get_bind_methods())() { \
return &m_class::_bind_methods; \
} \
+ _FORCE_INLINE_ static void (*_get_bind_compatibility_methods())() { \
+ return &m_class::_bind_compatibility_methods; \
+ } \
\
public: \
static void initialize_class() { \
@@ -442,6 +445,9 @@ public:
if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) { \
_bind_methods(); \
} \
+ if (m_class::_get_bind_compatibility_methods() != m_inherits::_get_bind_compatibility_methods()) { \
+ _bind_compatibility_methods(); \
+ } \
initialized = true; \
} \
\
@@ -674,6 +680,7 @@ protected:
virtual void _notificationv(int p_notification, bool p_reversed) {}
static void _bind_methods();
+ static void _bind_compatibility_methods() {}
bool _set(const StringName &p_name, const Variant &p_property) { return false; };
bool _get(const StringName &p_name, Variant &r_property) const { return false; };
void _get_property_list(List<PropertyInfo> *p_list) const {};
@@ -685,6 +692,9 @@ protected:
_FORCE_INLINE_ static void (*_get_bind_methods())() {
return &Object::_bind_methods;
}
+ _FORCE_INLINE_ static void (*_get_bind_compatibility_methods())() {
+ return &Object::_bind_compatibility_methods;
+ }
_FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &r_ret) const {
return &Object::_get;
}
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index d4d9387765..9fe8497eaf 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -202,4 +202,25 @@ public:
~WorkerThreadPool();
};
+template <typename F>
+static _FORCE_INLINE_ void for_range(int i_begin, int i_end, bool parallel, String name, F f) {
+ if (!parallel) {
+ for (int i = i_begin; i < i_end; i++) {
+ f(i);
+ }
+ return;
+ }
+
+ auto wrapper = [&](int i, void *unused) {
+ f(i + i_begin);
+ };
+
+ WorkerThreadPool *wtp = WorkerThreadPool::get_singleton();
+ WorkerThreadPool::GroupID gid = wtp->add_template_group_task(
+ &wrapper, &decltype(wrapper)::operator(), nullptr,
+ i_end - i_begin, -1,
+ true, name);
+ wtp->wait_for_group_task_completion(gid);
+}
+
#endif // WORKER_THREAD_POOL_H
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 67423128a3..38ea4a0fdd 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -505,6 +505,10 @@ bool OS::has_feature(const String &p_feature) {
return false;
}
+bool OS::is_sandboxed() const {
+ return false;
+}
+
void OS::set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments) {
restart_on_exit = p_restart;
restart_commandline = p_restart_arguments;
diff --git a/core/os/os.h b/core/os/os.h
index f2787d6381..965dc1f912 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -295,6 +295,8 @@ public:
bool has_feature(const String &p_feature);
+ virtual bool is_sandboxed() const;
+
void set_has_server_feature_callback(HasServerFeatureCallback p_callback);
void set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments);
@@ -304,6 +306,7 @@ public:
virtual bool request_permission(const String &p_name) { return true; }
virtual bool request_permissions() { return true; }
virtual Vector<String> get_granted_permissions() const { return Vector<String>(); }
+ virtual void revoke_granted_permissions() {}
// For recording / measuring benchmark data. Only enabled with tools
void set_use_benchmark(bool p_use_benchmark);
diff --git a/core/string/print_string.cpp b/core/string/print_string.cpp
index 7b90710308..dcdde3c175 100644
--- a/core/string/print_string.cpp
+++ b/core/string/print_string.cpp
@@ -164,6 +164,8 @@ void __print_line_rich(String p_string) {
p_string_ansi = p_string_ansi.replace("[/fgcolor]", "\u001b[39;49m");
}
+ p_string_ansi += "\u001b[0m"; // Reset.
+
OS::get_singleton()->print_rich("%s\n", p_string_ansi.utf8().get_data());
_global_lock();
diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h
index 4da73f1cfb..e1745110d7 100644
--- a/core/templates/hash_map.h
+++ b/core/templates/hash_map.h
@@ -353,6 +353,40 @@ public:
return true;
}
+ // Replace the key of an entry in-place, without invalidating iterators or changing the entries position during iteration.
+ // p_old_key must exist in the map and p_new_key must not, unless it is equal to p_old_key.
+ bool replace_key(const TKey &p_old_key, const TKey &p_new_key) {
+ if (p_old_key == p_new_key) {
+ return true;
+ }
+ uint32_t pos = 0;
+ ERR_FAIL_COND_V(_lookup_pos(p_new_key, pos), false);
+ ERR_FAIL_COND_V(!_lookup_pos(p_old_key, pos), false);
+ HashMapElement<TKey, TValue> *element = elements[pos];
+
+ // Delete the old entries in hashes and elements.
+ const uint32_t capacity = hash_table_size_primes[capacity_index];
+ const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index];
+ uint32_t next_pos = fastmod((pos + 1), capacity_inv, capacity);
+ while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity, capacity_inv) != 0) {
+ SWAP(hashes[next_pos], hashes[pos]);
+ SWAP(elements[next_pos], elements[pos]);
+ pos = next_pos;
+ next_pos = fastmod((pos + 1), capacity_inv, capacity);
+ }
+ hashes[pos] = EMPTY_HASH;
+ elements[pos] = nullptr;
+ // _insert_with_hash will increment this again.
+ num_elements--;
+
+ // Update the HashMapElement with the new key and reinsert it.
+ const_cast<TKey &>(element->data.key) = p_new_key;
+ uint32_t hash = _hash(p_new_key);
+ _insert_with_hash(hash, element);
+
+ return true;
+ }
+
// Reserves space for a number of elements, useful to avoid many resizes and rehashes.
// If adding a known (possibly large) number of elements at once, must be larger than old capacity.
void reserve(uint32_t p_new_capacity) {
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 5215142dd3..5a0ded6c01 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -454,17 +454,21 @@ Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const {
const int s = size();
- int begin = CLAMP(p_begin, -s, s);
+ if (s == 0 || (p_begin < -s && p_step < 0) || (p_begin >= s && p_step > 0)) {
+ return result;
+ }
+
+ int begin = CLAMP(p_begin, -s, s - 1);
if (begin < 0) {
begin += s;
}
- int end = CLAMP(p_end, -s, s);
+ int end = CLAMP(p_end, -s - 1, s);
if (end < 0) {
end += s;
}
- ERR_FAIL_COND_V_MSG(p_step > 0 && begin > end, result, "Slice is positive, but bounds is decreasing.");
- ERR_FAIL_COND_V_MSG(p_step < 0 && begin < end, result, "Slice is negative, but bounds is increasing.");
+ ERR_FAIL_COND_V_MSG(p_step > 0 && begin > end, result, "Slice step is positive, but bounds are decreasing.");
+ ERR_FAIL_COND_V_MSG(p_step < 0 && begin < end, result, "Slice step is negative, but bounds are increasing.");
int result_size = (end - begin) / p_step + (((end - begin) % p_step != 0) ? 1 : 0);
result.resize(result_size);
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index f62c44aa0d..999cc6fa29 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -584,6 +584,7 @@
If either [param begin] or [param end] are negative, they will be relative to the end of the array (i.e. [code]arr.slice(0, -2)[/code] is a shorthand for [code]arr.slice(0, arr.size() - 2)[/code]).
If specified, [param step] is the relative index between source elements. It can be negative, then [param begin] must be higher than [param end]. For example, [code][0, 1, 2, 3, 4, 5].slice(5, 1, -2)[/code] returns [code][5, 3][/code].
If [param deep] is true, each element will be copied by value rather than by reference.
+ [b]Note:[/b] To include the first element when [param step] is negative, use [code]arr.slice(begin, -arr.size() - 1, step)[/code] (i.e. [code][0, 1, 2].slice(1, -4, -1)[/code] returns [code][1, 0][/code]).
</description>
</method>
<method name="sort">
diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml
index 74523ee397..09f810cb77 100644
--- a/doc/classes/CPUParticles2D.xml
+++ b/doc/classes/CPUParticles2D.xml
@@ -285,6 +285,13 @@
Particle texture. If [code]null[/code], particles will be squares.
</member>
</members>
+ <signals>
+ <signal name="finished">
+ <description>
+ Emitted when all active particles have finished processing. When [member one_shot] is disabled, particles will process continuously, so this is never emitted.
+ </description>
+ </signal>
+ </signals>
<constants>
<constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder">
Particles are drawn in the order emitted.
diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml
index 1714ebec71..905f5475be 100644
--- a/doc/classes/CPUParticles3D.xml
+++ b/doc/classes/CPUParticles3D.xml
@@ -309,6 +309,13 @@
Minimum tangent acceleration.
</member>
</members>
+ <signals>
+ <signal name="finished">
+ <description>
+ Emitted when all active particles have finished processing. When [member one_shot] is disabled, particles will process continuously, so this is never emitted.
+ </description>
+ </signal>
+ </signals>
<constants>
<constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder">
Particles are drawn in the order emitted.
diff --git a/doc/classes/CollisionShape3D.xml b/doc/classes/CollisionShape3D.xml
index 171deb9c62..4e32545f27 100644
--- a/doc/classes/CollisionShape3D.xml
+++ b/doc/classes/CollisionShape3D.xml
@@ -20,11 +20,11 @@
Sets the collision shape's shape to the addition of all its convexed [MeshInstance3D] siblings geometry.
</description>
</method>
- <method name="resource_changed">
+ <method name="resource_changed" is_deprecated="true">
<return type="void" />
<param index="0" name="resource" type="Resource" />
<description>
- If this method exists within a script it will be called whenever the shape resource has been modified.
+ [i]Obsoleted.[/i] Use [signal Resource.changed] instead.
</description>
</method>
</methods>
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index b09c81d806..9c075a979a 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -96,6 +96,24 @@
[b]Note:[/b] This method is implemented only on Windows.
</description>
</method>
+ <method name="file_dialog_show">
+ <return type="int" enum="Error" />
+ <param index="0" name="title" type="String" />
+ <param index="1" name="current_directory" type="String" />
+ <param index="2" name="filename" type="String" />
+ <param index="3" name="show_hidden" type="bool" />
+ <param index="4" name="mode" type="int" enum="DisplayServer.FileDialogMode" />
+ <param index="5" name="filters" type="PackedStringArray" />
+ <param index="6" name="callback" type="Callable" />
+ <description>
+ Displays OS native dialog for selecting files or directories in the file system.
+ Callbacks have the following arguments: [code]bool status, PackedStringArray selected_paths[/code].
+ [b]Note:[/b] This method is implemented if the display server has the [code]FEATURE_NATIVE_DIALOG[/code] feature.
+ [b]Note:[/b] This method is implemented on Windows and macOS.
+ [b]Note:[/b] On macOS, native file dialogs have no title.
+ [b]Note:[/b] On macOS, sandboxed apps will save security-scoped bookmarks to retain access to the opened folders across multiple sessions. Use [method OS.get_granted_permissions] to get a list of saved bookmarks.
+ </description>
+ </method>
<method name="force_process_and_drop_events">
<return type="void" />
<description>
@@ -1729,6 +1747,21 @@
<constant name="CURSOR_MAX" value="17" enum="CursorShape">
Represents the size of the [enum CursorShape] enum.
</constant>
+ <constant name="FILE_DIALOG_MODE_OPEN_FILE" value="0" enum="FileDialogMode">
+ The native file dialog allows selecting one, and only one file.
+ </constant>
+ <constant name="FILE_DIALOG_MODE_OPEN_FILES" value="1" enum="FileDialogMode">
+ The native file dialog allows selecting multiple files.
+ </constant>
+ <constant name="FILE_DIALOG_MODE_OPEN_DIR" value="2" enum="FileDialogMode">
+ The native file dialog only allows selecting a directory, disallowing the selection of any file.
+ </constant>
+ <constant name="FILE_DIALOG_MODE_OPEN_ANY" value="3" enum="FileDialogMode">
+ The native file dialog allows selecting one file or directory.
+ </constant>
+ <constant name="FILE_DIALOG_MODE_SAVE_FILE" value="4" enum="FileDialogMode">
+ The native file dialog will warn when a file exists.
+ </constant>
<constant name="WINDOW_MODE_WINDOWED" value="0" enum="WindowMode">
Windowed mode, i.e. [Window] doesn't occupy the whole screen (unless set to the size of the screen).
</constant>
diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml
index fa82447894..75641a4486 100644
--- a/doc/classes/EditorExportPlugin.xml
+++ b/doc/classes/EditorExportPlugin.xml
@@ -8,6 +8,7 @@
To use [EditorExportPlugin], register it using the [method EditorPlugin.add_export_plugin] method first.
</description>
<tutorials>
+ <link title="Export Android plugins">$DOCS_URL/tutorials/platform/android/android_plugin.html</link>
</tutorials>
<methods>
<method name="_begin_customize_resources" qualifiers="virtual const">
@@ -84,6 +85,64 @@
Calling [method skip] inside this callback will make the file not included in the export.
</description>
</method>
+ <method name="_get_android_dependencies" qualifiers="virtual const">
+ <return type="PackedStringArray" />
+ <param index="0" name="platform" type="EditorExportPlatform" />
+ <param index="1" name="debug" type="bool" />
+ <description>
+ Virtual method to be overridden by the user. This is called to retrieve the set of Android dependencies provided by this plugin. Each returned Android dependency should have the format of an Android remote binary dependency: [code]org.godot.example:my-plugin:0.0.0[/code]
+ For more information see [url=https://developer.android.com/build/dependencies?agpversion=4.1#dependency-types]Android documentation on dependencies[/url].
+ [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled.
+ </description>
+ </method>
+ <method name="_get_android_dependencies_maven_repos" qualifiers="virtual const">
+ <return type="PackedStringArray" />
+ <param index="0" name="platform" type="EditorExportPlatform" />
+ <param index="1" name="debug" type="bool" />
+ <description>
+ Virtual method to be overridden by the user. This is called to retrieve the URLs of Maven repositories for the set of Android dependencies provided by this plugin.
+ For more information see [url=https://docs.gradle.org/current/userguide/dependency_management.html#sec:maven_repo]Gradle documentation on dependency management[/url].
+ [b]Note:[/b] Google's Maven repo and the Maven Central repo are already included by default.
+ [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled.
+ </description>
+ </method>
+ <method name="_get_android_libraries" qualifiers="virtual const">
+ <return type="PackedStringArray" />
+ <param index="0" name="platform" type="EditorExportPlatform" />
+ <param index="1" name="debug" type="bool" />
+ <description>
+ Virtual method to be overridden by the user. This is called to retrieve the local paths of the Android libraries archive (AAR) files provided by this plugin.
+ [b]Note:[/b] Relative paths **must** be relative to Godot's [code]res://addons/[/code] directory. For example, an AAR file located under [code]res://addons/hello_world_plugin/HelloWorld.release.aar[/code] can be returned as an absolute path using [code]res://addons/hello_world_plugin/HelloWorld.release.aar[/code] or a relative path using [code]hello_world_plugin/HelloWorld.release.aar[/code].
+ [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled.
+ </description>
+ </method>
+ <method name="_get_android_manifest_activity_element_contents" qualifiers="virtual const">
+ <return type="String" />
+ <param index="0" name="platform" type="EditorExportPlatform" />
+ <param index="1" name="debug" type="bool" />
+ <description>
+ Virtual method to be overridden by the user. This is used at export time to update the contents of the [code]activity[/code] element in the generated Android manifest.
+ [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled.
+ </description>
+ </method>
+ <method name="_get_android_manifest_application_element_contents" qualifiers="virtual const">
+ <return type="String" />
+ <param index="0" name="platform" type="EditorExportPlatform" />
+ <param index="1" name="debug" type="bool" />
+ <description>
+ Virtual method to be overridden by the user. This is used at export time to update the contents of the [code]application[/code] element in the generated Android manifest.
+ [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled.
+ </description>
+ </method>
+ <method name="_get_android_manifest_element_contents" qualifiers="virtual const">
+ <return type="String" />
+ <param index="0" name="platform" type="EditorExportPlatform" />
+ <param index="1" name="debug" type="bool" />
+ <description>
+ Virtual method to be overridden by the user. This is used at export time to update the contents of the [code]manifest[/code] element in the generated Android manifest.
+ [b]Note:[/b] Only supported on Android and requires [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] to be enabled.
+ </description>
+ </method>
<method name="_get_customization_configuration_hash" qualifiers="virtual const">
<return type="int" />
<description>
@@ -99,6 +158,15 @@
Return a [PackedStringArray] of additional features this preset, for the given [param platform], should have.
</description>
</method>
+ <method name="_get_export_option_warning" qualifiers="virtual const">
+ <return type="String" />
+ <param index="0" name="platform" type="EditorExportPlatform" />
+ <param index="1" name="option" type="String" />
+ <description>
+ Check the requirements for the given [param option] and return a non-empty warning string if they are not met.
+ [b]Note:[/b] Use [method get_option] to check the value of the export options.
+ </description>
+ </method>
<method name="_get_export_options" qualifiers="virtual const">
<return type="Dictionary[]" />
<param index="0" name="platform" type="EditorExportPlatform" />
@@ -124,6 +192,13 @@
Return [code]true[/code], if the result of [method _get_export_options] has changed and the export options of preset corresponding to [param platform] should be updated.
</description>
</method>
+ <method name="_supports_platform" qualifiers="virtual const">
+ <return type="bool" />
+ <param index="0" name="platform" type="EditorExportPlatform" />
+ <description>
+ Return [code]true[/code] if the plugin supports the given [param platform].
+ </description>
+ </method>
<method name="add_file">
<return type="void" />
<param index="0" name="path" type="String" />
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 957b6d8e88..3429062328 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -280,6 +280,34 @@
[/codeblock]
</description>
</method>
+ <method name="_get_unsaved_status" qualifiers="virtual const">
+ <return type="String" />
+ <param index="0" name="for_scene" type="String" />
+ <description>
+ Override this method to provide a custom message that lists unsaved changes. The editor will call this method when exiting or when closing a scene, and display the returned string in a confirmation dialog. Return empty string if the plugin has no unsaved changes.
+ When closing a scene, [param for_scene] is the path to the scene being closed. You can use it to handle built-in resources in that scene.
+ If the user confirms saving, [method _save_external_data] will be called, before closing the editor.
+ [codeblock]
+ func _get_unsaved_status(for_scene):
+ if not unsaved:
+ return ""
+
+ if for_scene.is_empty():
+ return "Save changes in MyCustomPlugin before closing?"
+ else:
+ return "Scene %s has changes from MyCustomPlugin. Save before closing?" % for_scene.get_file()
+
+ func _save_external_data():
+ unsaved = false
+ [/codeblock]
+ If the plugin has no scene-specific changes, you can ignore the calls when closing scenes:
+ [codeblock]
+ func _get_unsaved_status(for_scene):
+ if not for_scene.is_empty():
+ return ""
+ [/codeblock]
+ </description>
+ </method>
<method name="_get_window_layout" qualifiers="virtual">
<return type="void" />
<param index="0" name="configuration" type="ConfigFile" />
@@ -541,6 +569,12 @@
Returns the [PopupMenu] under [b]Scene &gt; Export As...[/b].
</description>
</method>
+ <method name="get_plugin_version" qualifiers="const">
+ <return type="String" />
+ <description>
+ Provide the version of the plugin declared in the [code]plugin.cfg[/code] config file.
+ </description>
+ </method>
<method name="get_script_create_dialog">
<return type="ScriptCreateDialog" />
<description>
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 05a3e22830..c7d05eb913 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -250,6 +250,9 @@
<member name="editors/2d/smart_snapping_line_color" type="Color" setter="" getter="">
The color to use when drawing smart snapping lines in the 2D editor. The smart snapping lines will automatically display when moving 2D nodes if smart snapping is enabled in the Snapping Options menu at the top of the 2D editor viewport.
</member>
+ <member name="editors/2d/use_integer_zoom_by_default" type="bool" setter="" getter="">
+ If [code]true[/code], the 2D editor will snap to integer zoom values while not holding the [kbd]Alt[/kbd] key and powers of two while holding it. If [code]false[/code], this behavior is swapped.
+ </member>
<member name="editors/2d/viewport_border_color" type="Color" setter="" getter="">
The color of the viewport border in the 2D editor. This border represents the viewport's size at the base resolution defined in the Project Settings. Objects placed outside this border will not be visible unless a [Camera2D] node is used, or unless the window is resized and the stretch mode is set to [code]disabled[/code].
</member>
diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml
index 1020d2dfbc..75856e91d8 100644
--- a/doc/classes/FileDialog.xml
+++ b/doc/classes/FileDialog.xml
@@ -83,6 +83,10 @@
If [code]true[/code], the dialog will show hidden files.
</member>
<member name="title" type="String" setter="set_title" getter="get_title" overrides="Window" default="&quot;Save a File&quot;" />
+ <member name="use_native_dialog" type="bool" setter="set_use_native_dialog" getter="get_use_native_dialog" default="false">
+ If [code]true[/code], [member access] is set to [constant ACCESS_FILESYSTEM], and it is supported by the current [DisplayServer], OS native dialog will be used instead of custom one.
+ [b]Note:[/b] On macOS, sandboxed apps always use native dialogs to access host filesystem.
+ </member>
</members>
<signals>
<signal name="dir_selected">
diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml
index c4193a5b01..e6d3c130ca 100644
--- a/doc/classes/GPUParticles2D.xml
+++ b/doc/classes/GPUParticles2D.xml
@@ -18,6 +18,7 @@
<return type="Rect2" />
<description>
Returns a rectangle containing the positions of all existing particles.
+ [b]Note:[/b] When using threaded rendering this method synchronizes the rendering thread. Calling it often may have a negative impact on performance.
</description>
</method>
<method name="emit_particle">
@@ -108,6 +109,14 @@
Grow the rect if particles suddenly appear/disappear when the node enters/exits the screen. The [Rect2] can be grown via code or with the [b]Particles → Generate Visibility Rect[/b] editor tool.
</member>
</members>
+ <signals>
+ <signal name="finished">
+ <description>
+ Emitted when all active particles have finished processing. When [member one_shot] is disabled, particles will process continuously, so this is never emitted.
+ [b]Note:[/b] Due to the particles being computed on the GPU there might be a delay before the signal gets emitted.
+ </description>
+ </signal>
+ </signals>
<constants>
<constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder">
Particles are drawn in the order emitted.
diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml
index 8338535a24..0dc7c23315 100644
--- a/doc/classes/GPUParticles3D.xml
+++ b/doc/classes/GPUParticles3D.xml
@@ -130,6 +130,14 @@
Grow the box if particles suddenly appear/disappear when the node enters/exits the screen. The [AABB] can be grown via code or with the [b]Particles → Generate AABB[/b] editor tool.
</member>
</members>
+ <signals>
+ <signal name="finished">
+ <description>
+ Emitted when all active particles have finished processing. When [member one_shot] is disabled, particles will process continuously, so this is never emitted.
+ [b]Note:[/b] Due to the particles being computed on the GPU there might be a delay before the signal gets emitted.
+ </description>
+ </signal>
+ </signals>
<constants>
<constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder">
Particles are drawn in the order emitted.
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index d8d0078b77..03169d390a 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -240,8 +240,8 @@
<method name="get_granted_permissions" qualifiers="const">
<return type="PackedStringArray" />
<description>
- With this function, you can get the list of dangerous permissions that have been granted to the Android application.
- [b]Note:[/b] This method is implemented only on Android.
+ On Android devices: With this function, you can get the list of dangerous permissions that have been granted.
+ On macOS (sandboxed applications only): This function returns the list of user selected folders accessible to the application. Use native file dialog to request folder access permission.
</description>
</method>
<method name="get_keycode_string" qualifiers="const">
@@ -534,6 +534,13 @@
Returns [code]true[/code] if the project will automatically restart when it exits for any reason, [code]false[/code] otherwise. See also [method set_restart_on_exit] and [method get_restart_on_exit_arguments].
</description>
</method>
+ <method name="is_sandboxed" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if application is running in the sandbox.
+ [b]Note:[/b] This method is implemented on macOS.
+ </description>
+ </method>
<method name="is_stdout_verbose" qualifiers="const">
<return type="bool" />
<description>
@@ -602,6 +609,12 @@
[b]Note:[/b] This method is implemented only on Android.
</description>
</method>
+ <method name="revoke_granted_permissions">
+ <return type="void" />
+ <description>
+ On macOS (sandboxed applications only), this function clears list of user selected folders accessible to the application.
+ </description>
+ </method>
<method name="set_environment" qualifiers="const">
<return type="void" />
<param index="0" name="variable" type="String" />
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 744c72af4d..0c78525e45 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1943,13 +1943,13 @@
<member name="memory/limits/multithreaded_server/rid_pool_prealloc" type="int" setter="" getter="" default="60">
This is used by servers when used in multi-threading mode (servers and visual). RIDs are preallocated to avoid stalling the server requesting them on threads. If servers get stalled too often when loading resources in a thread, increase this number.
</member>
- <member name="navigation/2d/default_cell_size" type="int" setter="" getter="" default="1">
+ <member name="navigation/2d/default_cell_size" type="float" setter="" getter="" default="1.0">
Default cell size for 2D navigation maps. See [method NavigationServer2D.map_set_cell_size].
</member>
- <member name="navigation/2d/default_edge_connection_margin" type="int" setter="" getter="" default="1">
+ <member name="navigation/2d/default_edge_connection_margin" type="float" setter="" getter="" default="1.0">
Default edge connection margin for 2D navigation maps. See [method NavigationServer2D.map_set_edge_connection_margin].
</member>
- <member name="navigation/2d/default_link_connection_radius" type="int" setter="" getter="" default="4">
+ <member name="navigation/2d/default_link_connection_radius" type="float" setter="" getter="" default="4.0">
Default link connection radius for 2D navigation maps. See [method NavigationServer2D.map_set_link_connection_radius].
</member>
<member name="navigation/2d/use_edge_connections" type="bool" setter="" getter="" default="true">
diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml
index 16471036cd..83476a6d48 100644
--- a/doc/classes/RayCast3D.xml
+++ b/doc/classes/RayCast3D.xml
@@ -131,6 +131,9 @@
<member name="exclude_parent" type="bool" setter="set_exclude_parent_body" getter="get_exclude_parent_body" default="true">
If [code]true[/code], collisions will be ignored for this RayCast3D's immediate parent.
</member>
+ <member name="hit_back_faces" type="bool" setter="set_hit_back_faces" getter="is_hit_back_faces_enabled" default="true">
+ If [code]true[/code], the ray will hit back faces with concave polygon shapes with back face enabled or heightmap shapes.
+ </member>
<member name="hit_from_inside" type="bool" setter="set_hit_from_inside" getter="is_hit_from_inside_enabled" default="false">
If [code]true[/code], the ray will detect a hit when starting inside shapes. In this case the collision normal will be [code]Vector3(0, 0, 0)[/code]. Does not affect shapes with no volume like concave polygon or heightmap.
</member>
diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index ab993c372a..73507b10a7 100644
--- a/doc/classes/RenderingDevice.xml
+++ b/doc/classes/RenderingDevice.xml
@@ -16,8 +16,8 @@
<methods>
<method name="barrier">
<return type="void" />
- <param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" />
- <param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" />
+ <param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
+ <param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
<description>
Puts a memory barrier in place. This is used for synchronization to avoid data races. See also [method full_barrier], which may be useful for debugging.
</description>
@@ -27,7 +27,7 @@
<param index="0" name="buffer" type="RID" />
<param index="1" name="offset" type="int" />
<param index="2" name="size_bytes" type="int" />
- <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" />
+ <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
<description>
</description>
</method>
@@ -46,7 +46,7 @@
<param index="1" name="offset" type="int" />
<param index="2" name="size_bytes" type="int" />
<param index="3" name="data" type="PackedByteArray" />
- <param index="4" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" />
+ <param index="4" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
<description>
</description>
</method>
@@ -114,7 +114,7 @@
</method>
<method name="compute_list_end">
<return type="void" />
- <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" />
+ <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
<description>
Finishes a list of compute commands created with the [code]compute_*[/code] methods.
</description>
@@ -296,7 +296,7 @@
</method>
<method name="draw_list_end">
<return type="void" />
- <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" />
+ <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
<description>
Finishes a list of raster drawing commands created with the [code]draw_*[/code] methods.
</description>
@@ -682,7 +682,7 @@
<param index="3" name="mipmap_count" type="int" />
<param index="4" name="base_layer" type="int" />
<param index="5" name="layer_count" type="int" />
- <param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" />
+ <param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
<description>
Clears the specified [param texture] by replacing all of its pixels with the specified [param color]. [param base_mipmap] and [param mipmap_count] determine which mipmaps of the texture are affected by this clear operation, while [param base_layer] and [param layer_count] determine which layers of a 3D texture (or texture array) are affected by this clear operation. For 2D textures (which only have one layer by design), [param base_layer] and [param layer_count] must both be [code]0[/code].
[b]Note:[/b] [param texture] can't be cleared while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to clear this texture.
@@ -699,7 +699,7 @@
<param index="6" name="dst_mipmap" type="int" />
<param index="7" name="src_layer" type="int" />
<param index="8" name="dst_layer" type="int" />
- <param index="9" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" />
+ <param index="9" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
<description>
Copies the [param from_texture] to [param to_texture] with the specified [param from_pos], [param to_pos] and [param size] coordinates. The Z axis of the [param from_pos], [param to_pos] and [param size] must be [code]0[/code] for 2-dimensional textures. Source and destination mipmaps/layers must also be specified, with these parameters being [code]0[/code] for textures without mipmaps or single-layer textures. Returns [constant @GlobalScope.OK] if the texture copy was successful or [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise.
[b]Note:[/b] [param from_texture] texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to copy this texture.
@@ -786,7 +786,7 @@
<return type="int" enum="Error" />
<param index="0" name="from_texture" type="RID" />
<param index="1" name="to_texture" type="RID" />
- <param index="2" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" />
+ <param index="2" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
<description>
Resolves the [param from_texture] texture onto [param to_texture] with multisample antialiasing enabled. This must be used when rendering a framebuffer for MSAA to work. Returns [constant @GlobalScope.OK] if successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise.
[b]Note:[/b] [param from_texture] and [param to_texture] textures must have the same dimension, format and type (color or depth).
@@ -803,7 +803,7 @@
<param index="0" name="texture" type="RID" />
<param index="1" name="layer" type="int" />
<param index="2" name="data" type="PackedByteArray" />
- <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="7" />
+ <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="15" />
<description>
Updates texture data with new data, replacing the previous data in place. The updated texture data must have the same dimensions and format. For 2D textures (which only have one layer), [param layer] must be [code]0[/code]. Returns [constant @GlobalScope.OK] if the update was successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise.
[b]Note:[/b] Updating textures is forbidden during creation of a draw or compute list.
@@ -1580,19 +1580,25 @@
<constant name="DATA_FORMAT_MAX" value="218" enum="DataFormat">
Represents the size of the [enum DataFormat] enum.
</constant>
- <constant name="BARRIER_MASK_RASTER" value="1" enum="BarrierMask" is_bitfield="true">
- Raster barrier mask.
+ <constant name="BARRIER_MASK_VERTEX" value="1" enum="BarrierMask" is_bitfield="true">
+ Vertex shader barrier mask.
</constant>
- <constant name="BARRIER_MASK_COMPUTE" value="2" enum="BarrierMask" is_bitfield="true">
+ <constant name="BARRIER_MASK_FRAGMENT" value="2" enum="BarrierMask" is_bitfield="true">
+ Fragment shader barrier mask.
+ </constant>
+ <constant name="BARRIER_MASK_COMPUTE" value="4" enum="BarrierMask" is_bitfield="true">
Compute barrier mask.
</constant>
- <constant name="BARRIER_MASK_TRANSFER" value="4" enum="BarrierMask" is_bitfield="true">
+ <constant name="BARRIER_MASK_TRANSFER" value="8" enum="BarrierMask" is_bitfield="true">
Transfer barrier mask.
</constant>
- <constant name="BARRIER_MASK_ALL_BARRIERS" value="7" enum="BarrierMask" is_bitfield="true">
+ <constant name="BARRIER_MASK_RASTER" value="3" enum="BarrierMask" is_bitfield="true">
+ Raster barrier mask (vertex and fragment). Equivalent to [code]BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT[/code].
+ </constant>
+ <constant name="BARRIER_MASK_ALL_BARRIERS" value="15" enum="BarrierMask" is_bitfield="true">
Barrier mask for all types (raster, compute, transfer). Equivalent to [code]BARRIER_MASK_RASTER | BARRIER_MASK_COMPUTE | BARRIER_MASK_TRANSFER[/code].
</constant>
- <constant name="BARRIER_MASK_NO_BARRIER" value="8" enum="BarrierMask" is_bitfield="true">
+ <constant name="BARRIER_MASK_NO_BARRIER" value="16" enum="BarrierMask" is_bitfield="true">
No barrier for any type.
</constant>
<constant name="TEXTURE_TYPE_1D" value="0" enum="TextureType">
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 388f4dfc18..e2030f5927 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -266,6 +266,18 @@
Terminates the current tag. Use after [code]push_*[/code] methods to close BBCodes manually. Does not need to follow [code]add_*[/code] methods.
</description>
</method>
+ <method name="pop_all">
+ <return type="void" />
+ <description>
+ Terminates all tags opened by [code]push_*[/code] methods.
+ </description>
+ </method>
+ <method name="pop_context">
+ <return type="void" />
+ <description>
+ Terminates tags opened after the last [method push_context] call (including context marker), or all tags if there's no context marker on the stack.
+ </description>
+ </method>
<method name="push_bgcolor">
<return type="void" />
<param index="0" name="bgcolor" type="Color" />
@@ -298,6 +310,12 @@
Adds a [code][color][/code] tag to the tag stack.
</description>
</method>
+ <method name="push_context">
+ <return type="void" />
+ <description>
+ Adds a context marker to the tag stack. See [method pop_context].
+ </description>
+ </method>
<method name="push_customfx">
<return type="void" />
<param index="0" name="effect" type="RichTextEffect" />
diff --git a/doc/classes/ShapeCast3D.xml b/doc/classes/ShapeCast3D.xml
index 4bbf763a6e..ce72944098 100644
--- a/doc/classes/ShapeCast3D.xml
+++ b/doc/classes/ShapeCast3D.xml
@@ -119,11 +119,11 @@
Removes a collision exception so the shape does report collisions with the specified [RID].
</description>
</method>
- <method name="resource_changed">
+ <method name="resource_changed" is_deprecated="true">
<return type="void" />
<param index="0" name="resource" type="Resource" />
<description>
- This method is used internally to update the debug gizmo in the editor. Any code placed in this function will be called whenever the [member shape] resource is modified.
+ [i]Obsoleted.[/i] Use [signal Resource.changed] instead.
</description>
</method>
<method name="set_collision_mask_value">
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 134022866c..4ed831c213 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -55,6 +55,7 @@
<param index="0" name="layer" type="int" />
<description>
Clears all cells on the given layer.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="erase_cell">
@@ -63,6 +64,7 @@
<param index="1" name="coords" type="Vector2i" />
<description>
Erases the cell on layer [param layer] at coordinates [param coords].
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="fix_invalid_tiles">
@@ -75,7 +77,7 @@
<return type="void" />
<param index="0" name="layer" type="int" default="-1" />
<description>
- Triggers an update of the TileMap. If [param layer] is provided, only updates the given layer.
+ Triggers an update of the TileMap. If [param layer] is provided and is positive, only updates the given layer.
[b]Note:[/b] The TileMap node updates automatically when one of its properties is modified. A manual update is only needed if runtime modifications (implemented in [method _tile_data_runtime_update]) need to be applied.
[b]Warning:[/b] Updating the TileMap is computationally expensive and may impact performance. Try to limit the number of updates and the tiles they impact (by placing frequently updated tiles in a dedicated layer for example).
</description>
@@ -87,6 +89,7 @@
<param index="2" name="use_proxies" type="bool" default="false" />
<description>
Returns the tile alternative ID of the cell on layer [param layer] at [param coords]. If [param use_proxies] is [code]false[/code], ignores the [TileSet]'s tile proxies, returning the raw alternative identifier. See [method TileSet.map_tile_proxy].
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="get_cell_atlas_coords" qualifiers="const">
@@ -96,6 +99,7 @@
<param index="2" name="use_proxies" type="bool" default="false" />
<description>
Returns the tile atlas coordinates ID of the cell on layer [param layer] at coordinates [param coords]. If [param use_proxies] is [code]false[/code], ignores the [TileSet]'s tile proxies, returning the raw alternative identifier. See [method TileSet.map_tile_proxy].
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="get_cell_source_id" qualifiers="const">
@@ -106,6 +110,7 @@
<description>
Returns the tile source ID of the cell on layer [param layer] at coordinates [param coords]. Returns [code]-1[/code] if the cell does not exist.
If [param use_proxies] is [code]false[/code], ignores the [TileSet]'s tile proxies, returning the raw alternative identifier. See [method TileSet.map_tile_proxy].
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="get_cell_tile_data" qualifiers="const">
@@ -115,6 +120,7 @@
<param index="2" name="use_proxies" type="bool" default="false" />
<description>
Returns the [TileData] object associated with the given cell, or [code]null[/code] if the cell does not exist or is not a [TileSetAtlasSource].
+ If [param layer] is negative, the layers are accessed from the last one.
If [param use_proxies] is [code]false[/code], ignores the [TileSet]'s tile proxies, returning the raw alternative identifier. See [method TileSet.map_tile_proxy].
[codeblock]
func get_clicked_tile_power():
@@ -146,6 +152,7 @@
<param index="0" name="layer" type="int" />
<description>
Returns a TileMap layer's modulate.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="get_layer_name" qualifiers="const">
@@ -153,6 +160,17 @@
<param index="0" name="layer" type="int" />
<description>
Returns a TileMap layer's name.
+ If [param layer] is negative, the layers are accessed from the last one.
+ </description>
+ </method>
+ <method name="get_layer_navigation_map" qualifiers="const">
+ <return type="RID" />
+ <param index="0" name="layer" type="int" />
+ <description>
+ Returns the [NavigationServer2D] navigation map [RID] currently assigned to the specified TileMap [param layer].
+ By default the TileMap uses the default [World2D] navigation map for the first TileMap layer. For each additional TileMap layer a new navigation map is created for the additional layer.
+ In order to make [NavigationAgent2D] switch between TileMap layer navigation maps use [method NavigationAgent2D.set_navigation_map] with the navigation map received from [method get_layer_navigation_map].
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="get_layer_y_sort_origin" qualifiers="const">
@@ -160,6 +178,7 @@
<param index="0" name="layer" type="int" />
<description>
Returns a TileMap layer's Y sort origin.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="get_layer_z_index" qualifiers="const">
@@ -167,6 +186,7 @@
<param index="0" name="layer" type="int" />
<description>
Returns a TileMap layer's Z-index value.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="get_layers_count" qualifiers="const">
@@ -175,13 +195,11 @@
Returns the number of layers in the TileMap.
</description>
</method>
- <method name="get_navigation_map" qualifiers="const">
+ <method name="get_navigation_map" qualifiers="const" is_deprecated="true">
<return type="RID" />
<param index="0" name="layer" type="int" />
<description>
- Returns the [NavigationServer2D] navigation map [RID] currently assigned to the specified TileMap [param layer].
- By default the TileMap uses the default [World2D] navigation map for the first TileMap layer. For each additional TileMap layer a new navigation map is created for the additional layer.
- In order to make [NavigationAgent2D] switch between TileMap layer navigation maps use [method NavigationAgent2D.set_navigation_map] with the navigation map received from [method get_navigation_map].
+ See [method get_layer_navigation_map].
</description>
</method>
<method name="get_neighbor_cell" qualifiers="const">
@@ -198,6 +216,7 @@
<param index="1" name="coords_array" type="Vector2i[]" />
<description>
Creates a new [TileMapPattern] from the given layer and set of cells.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="get_surrounding_cells">
@@ -212,6 +231,7 @@
<param index="0" name="layer" type="int" />
<description>
Returns a [Vector2i] array with the positions of all cells containing a tile in the given layer. A cell is considered empty if its source identifier equals -1, its atlas coordinates identifiers is [code]Vector2(-1, -1)[/code] and its alternative identifier is -1.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="get_used_cells_by_id" qualifiers="const">
@@ -224,9 +244,10 @@
Returns a [Vector2i] array with the positions of all cells containing a tile in the given layer. Tiles may be filtered according to their source ([param source_id]), their atlas coordinates ([param atlas_coords]) or alternative id ([param alternative_tile]).
If a parameter has it's value set to the default one, this parameter is not used to filter a cell. Thus, if all parameters have their respective default value, this method returns the same result as [method get_used_cells].
A cell is considered empty if its source identifier equals -1, its atlas coordinates identifiers is [code]Vector2(-1, -1)[/code] and its alternative identifier is -1.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
- <method name="get_used_rect">
+ <method name="get_used_rect" qualifiers="const">
<return type="Rect2i" />
<description>
Returns a rectangle enclosing the used (non-empty) tiles of the map, including all layers.
@@ -237,6 +258,7 @@
<param index="0" name="layer" type="int" />
<description>
Returns if a layer is enabled.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="is_layer_y_sort_enabled" qualifiers="const">
@@ -244,6 +266,7 @@
<param index="0" name="layer" type="int" />
<description>
Returns if a layer Y-sorts its tiles.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="local_to_map" qualifiers="const">
@@ -298,6 +321,7 @@
- The atlas coordinates identifier [param atlas_coords] identifies a tile coordinates in the atlas (if the source is a [TileSetAtlasSource]). For [TileSetScenesCollectionSource] it should always be [code]Vector2i(0, 0)[/code]),
- The alternative tile identifier [param alternative_tile] identifies a tile alternative in the atlas (if the source is a [TileSetAtlasSource]), and the scene for a [TileSetScenesCollectionSource].
If [param source_id] is set to [code]-1[/code], [param atlas_coords] to [code]Vector2i(-1, -1)[/code] or [param alternative_tile] to [code]-1[/code], the cell will be erased. An erased cell gets [b]all[/b] its identifiers automatically set to their respective invalid values, namely [code]-1[/code], [code]Vector2i(-1, -1)[/code] and [code]-1[/code].
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
<method name="set_cells_terrain_connect">
@@ -310,6 +334,7 @@
<description>
Update all the cells in the [param cells] coordinates array so that they use the given [param terrain] for the given [param terrain_set]. If an updated cell has the same terrain as one of its neighboring cells, this function tries to join the two. This function might update neighboring tiles if needed to create correct terrain transitions.
If [param ignore_empty_terrains] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
+ If [param layer] is negative, the layers are accessed from the last one.
[b]Note:[/b] To work correctly, this method requires the TileMap's TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results.
</description>
</method>
@@ -323,6 +348,7 @@
<description>
Update all the cells in the [param path] coordinates array so that they use the given [param terrain] for the given [param terrain_set]. The function will also connect two successive cell in the path with the same terrain. This function might update neighboring tiles if needed to create correct terrain transitions.
If [param ignore_empty_terrains] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints.
+ If [param layer] is negative, the layers are accessed from the last one.
[b]Note:[/b] To work correctly, this method requires the TileMap's TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results.
</description>
</method>
@@ -353,6 +379,17 @@
If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
+ <method name="set_layer_navigation_map">
+ <return type="void" />
+ <param index="0" name="layer" type="int" />
+ <param index="1" name="map" type="RID" />
+ <description>
+ Assigns a [NavigationServer2D] navigation map [RID] to the specified TileMap [param layer].
+ By default the TileMap uses the default [World2D] navigation map for the first TileMap layer. For each additional TileMap layer a new navigation map is created for the additional layer.
+ In order to make [NavigationAgent2D] switch between TileMap layer navigation maps use [method NavigationAgent2D.set_navigation_map] with the navigation map received from [method get_layer_navigation_map].
+ If [param layer] is negative, the layers are accessed from the last one.
+ </description>
+ </method>
<method name="set_layer_y_sort_enabled">
<return type="void" />
<param index="0" name="layer" type="int" />
@@ -382,14 +419,12 @@
If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
- <method name="set_navigation_map">
+ <method name="set_navigation_map" is_deprecated="true">
<return type="void" />
<param index="0" name="layer" type="int" />
<param index="1" name="map" type="RID" />
<description>
- Assigns a [NavigationServer2D] navigation map [RID] to the specified TileMap [param layer].
- By default the TileMap uses the default [World2D] navigation map for the first TileMap layer. For each additional TileMap layer a new navigation map is created for the additional layer.
- In order to make [NavigationAgent2D] switch between TileMap layer navigation maps use [method NavigationAgent2D.set_navigation_map] with the navigation map received from [method get_navigation_map].
+ See [method set_layer_navigation_map].
</description>
</method>
<method name="set_pattern">
@@ -399,6 +434,7 @@
<param index="2" name="pattern" type="TileMapPattern" />
<description>
Paste the given [TileMapPattern] at the given [param position] and [param layer] in the tile map.
+ If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
</methods>
diff --git a/doc/classes/VisualShaderNode.xml b/doc/classes/VisualShaderNode.xml
index 6b188a607d..5d147bd542 100644
--- a/doc/classes/VisualShaderNode.xml
+++ b/doc/classes/VisualShaderNode.xml
@@ -16,6 +16,13 @@
Clears the default input ports value.
</description>
</method>
+ <method name="get_default_input_port" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="type" type="int" enum="VisualShaderNode.PortType" />
+ <description>
+ Returns the input port which should be connected by default when this node is created as a result of dragging a connection from an existing node to the empty space on the graph.
+ </description>
+ </method>
<method name="get_default_input_values" qualifiers="const">
<return type="Array" />
<description>
diff --git a/doc/classes/VisualShaderNodeCustom.xml b/doc/classes/VisualShaderNodeCustom.xml
index 480f7dfe0e..8a90d5dd0f 100644
--- a/doc/classes/VisualShaderNodeCustom.xml
+++ b/doc/classes/VisualShaderNodeCustom.xml
@@ -37,6 +37,14 @@
Defining this method is [b]required[/b].
</description>
</method>
+ <method name="_get_default_input_port" qualifiers="virtual const">
+ <return type="int" />
+ <param index="0" name="type" type="int" enum="VisualShaderNode.PortType" />
+ <description>
+ Override this method to define the input port which should be connected by default when this node is created as a result of dragging a connection from an existing node to the empty space on the graph.
+ Defining this method is [b]optional[/b]. If not overridden, the connection will be created to the first valid port.
+ </description>
+ </method>
<method name="_get_description" qualifiers="virtual const">
<return type="String" />
<description>
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 0446f6d73f..4114a83584 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -903,5 +903,8 @@
The background style used when the [Window] is embedded. Note that this is drawn only under the window's content, excluding the title. For proper borders and title bar style, you can use [code]expand_margin_*[/code] properties of [StyleBoxFlat].
[b]Note:[/b] The content background will not be visible unless [member transparent] is enabled.
</theme_item>
+ <theme_item name="embedded_unfocused_border" data_type="style" type="StyleBox">
+ The background style used when the [Window] is embedded and unfocused.
+ </theme_item>
</theme_items>
</class>
diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py
index 376addcff0..717a468b36 100755
--- a/doc/tools/doc_status.py
+++ b/doc/tools/doc_status.py
@@ -383,12 +383,6 @@ for file in input_file_list:
tree = ET.parse(file)
doc = tree.getroot()
- if "version" not in doc.attrib:
- print('Version missing from "doc"')
- sys.exit(255)
-
- version = doc.attrib["version"]
-
if doc.attrib["name"] in class_names:
continue
class_names.append(doc.attrib["name"])
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index 28d30a594d..54bad7cf05 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -66,6 +66,15 @@ BASE_STRINGS = [
"This method doesn't need an instance to be called, so it can be called directly using the class name.",
"This method describes a valid operator to use with this type as left-hand operand.",
"This value is an integer composed as a bitmask of the following flags.",
+ "There is currently no description for this class. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
+ "There is currently no description for this signal. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
+ "There is currently no description for this annotation. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
+ "There is currently no description for this property. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
+ "There is currently no description for this constructor. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
+ "There is currently no description for this method. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
+ "There is currently no description for this operator. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
+ "There is currently no description for this theme property. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
+ "There are notable differences when using this API with C#. See :ref:`doc_c_sharp_differences` for more information.",
]
strings_l10n: Dict[str, str] = {}
@@ -92,6 +101,36 @@ EDITOR_CLASSES: List[str] = [
"ScriptEditor",
"ScriptEditorBase",
]
+# Sync with the types mentioned in https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/c_sharp_differences.html
+CLASSES_WITH_CSHARP_DIFFERENCES: List[str] = [
+ "@GlobalScope",
+ "String",
+ "NodePath",
+ "Signal",
+ "Callable",
+ "RID",
+ "Basis",
+ "Transform2D",
+ "Transform3D",
+ "Rect2",
+ "Rect2i",
+ "AABB",
+ "Quaternion",
+ "Projection",
+ "Color",
+ "Array",
+ "Dictionary",
+ "PackedByteArray",
+ "PackedColorArray",
+ "PackedFloat32Array",
+ "PackedFloat64Array",
+ "PackedInt32Array",
+ "PackedInt64Array",
+ "PackedStringArray",
+ "PackedVector2Array",
+ "PackedVector3Array",
+ "Variant",
+]
class State:
@@ -842,6 +881,15 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
+ "\n\n"
)
+ if class_def.name in CLASSES_WITH_CSHARP_DIFFERENCES:
+ f.write(".. note::\n\n\t")
+ f.write(
+ translate(
+ "There are notable differences when using this API with C#. See :ref:`doc_c_sharp_differences` for more information."
+ )
+ + "\n\n"
+ )
+
# Online tutorials
if len(class_def.tutorials) > 0:
f.write(".. rst-class:: classref-introduction-group\n\n")
diff --git a/drivers/png/resource_saver_png.cpp b/drivers/png/resource_saver_png.cpp
index ab0ff32514..0df6b2ba21 100644
--- a/drivers/png/resource_saver_png.cpp
+++ b/drivers/png/resource_saver_png.cpp
@@ -33,7 +33,7 @@
#include "core/io/file_access.h"
#include "core/io/image.h"
#include "drivers/png/png_driver_common.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/image_texture.h"
Error ResourceSaverPNG::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Ref<ImageTexture> texture = p_resource;
diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp
index b46af012f1..a8074aa3f6 100644
--- a/drivers/unix/net_socket_posix.cpp
+++ b/drivers/unix/net_socket_posix.cpp
@@ -204,6 +204,9 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
if (err == WSAEACCES) {
return ERR_NET_UNAUTHORIZED;
}
+ if (err == WSAEMSGSIZE || err == WSAENOBUFS) {
+ return ERR_NET_BUFFER_TOO_SMALL;
+ }
print_verbose("Socket error: " + itos(err));
return ERR_NET_OTHER;
#else
@@ -222,6 +225,9 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
if (errno == EACCES) {
return ERR_NET_UNAUTHORIZED;
}
+ if (errno == ENOBUFS) {
+ return ERR_NET_BUFFER_TOO_SMALL;
+ }
print_verbose("Socket error: " + itos(errno));
return ERR_NET_OTHER;
#endif
@@ -550,6 +556,10 @@ Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
return ERR_BUSY;
}
+ if (err == ERR_NET_BUFFER_TOO_SMALL) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
return FAILED;
}
@@ -571,6 +581,10 @@ Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddr
return ERR_BUSY;
}
+ if (err == ERR_NET_BUFFER_TOO_SMALL) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
return FAILED;
}
@@ -606,6 +620,9 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
if (err == ERR_NET_WOULD_BLOCK) {
return ERR_BUSY;
}
+ if (err == ERR_NET_BUFFER_TOO_SMALL) {
+ return ERR_OUT_OF_MEMORY;
+ }
return FAILED;
}
@@ -625,6 +642,9 @@ Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP
if (err == ERR_NET_WOULD_BLOCK) {
return ERR_BUSY;
}
+ if (err == ERR_NET_BUFFER_TOO_SMALL) {
+ return ERR_OUT_OF_MEMORY;
+ }
return FAILED;
}
diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h
index bd2088b4f9..2682530e15 100644
--- a/drivers/unix/net_socket_posix.h
+++ b/drivers/unix/net_socket_posix.h
@@ -56,6 +56,7 @@ private:
ERR_NET_IN_PROGRESS,
ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE,
ERR_NET_UNAUTHORIZED,
+ ERR_NET_BUFFER_TOO_SMALL,
ERR_NET_OTHER,
};
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 23c6919854..6cf8f2dfac 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -54,9 +54,13 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID
r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
r_access_mask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
if (buffer->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
- if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
+ if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) {
r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
- r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ }
+ if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) {
+ r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) {
r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
@@ -68,8 +72,11 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID
r_access_mask |= VK_ACCESS_INDEX_READ_BIT;
buffer = index_buffer_owner.get_or_null(p_buffer);
} else if (uniform_buffer_owner.owns(p_buffer)) {
- if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
- r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) {
+ r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ }
+ if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) {
+ r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) {
r_stage_mask |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
@@ -77,8 +84,12 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID
r_access_mask |= VK_ACCESS_UNIFORM_READ_BIT;
buffer = uniform_buffer_owner.get_or_null(p_buffer);
} else if (texture_buffer_owner.owns(p_buffer)) {
- if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
- r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) {
+ r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ r_access_mask |= VK_ACCESS_SHADER_READ_BIT;
+ }
+ if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) {
+ r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
r_access_mask |= VK_ACCESS_SHADER_READ_BIT;
}
if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) {
@@ -89,8 +100,12 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID
buffer = &texture_buffer_owner.get_or_null(p_buffer)->buffer;
} else if (storage_buffer_owner.owns(p_buffer)) {
buffer = storage_buffer_owner.get_or_null(p_buffer);
- if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
- r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) {
+ r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ }
+ if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) {
+ r_stage_mask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) {
@@ -2625,8 +2640,12 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co
barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
- if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
- barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) {
+ barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ }
+ if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) {
+ barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) {
@@ -3020,8 +3039,12 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture,
barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
- if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
- barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) {
+ barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ }
+ if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) {
+ barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) {
@@ -3198,8 +3221,12 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID
barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
- if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
- barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) {
+ barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ }
+ if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) {
+ barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) {
@@ -3334,8 +3361,12 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color,
barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
- if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
- barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) {
+ barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ }
+ if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) {
+ barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) {
@@ -7651,10 +7682,14 @@ void RenderingDeviceVulkan::draw_list_end(BitField<BarrierMask> p_post_barrier)
barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
- if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
- barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT /*| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT*/;
+ if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) {
+ barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT /*| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT*/;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT /*| VK_ACCESS_INDIRECT_COMMAND_READ_BIT*/;
}
+ if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) {
+ barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT /*| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT*/;
+ access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT /*| VK_ACCESS_INDIRECT_COMMAND_READ_BIT*/;
+ }
if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) {
barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;
@@ -8199,10 +8234,14 @@ void RenderingDeviceVulkan::compute_list_end(BitField<BarrierMask> p_post_barrie
barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
- if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
- barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
+ if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) {
+ barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
}
+ if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) {
+ barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
+ access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+ }
if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) {
barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;
@@ -8227,7 +8266,7 @@ void RenderingDeviceVulkan::barrier(BitField<BarrierMask> p_from, BitField<Barri
src_barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
src_access_flags |= VK_ACCESS_SHADER_WRITE_BIT;
}
- if (p_from.has_flag(BARRIER_MASK_RASTER)) {
+ if (p_from.has_flag(BARRIER_MASK_FRAGMENT)) {
src_barrier_flags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
src_access_flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
}
@@ -8247,10 +8286,14 @@ void RenderingDeviceVulkan::barrier(BitField<BarrierMask> p_from, BitField<Barri
dst_barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
dst_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
- if (p_to.has_flag(BARRIER_MASK_RASTER)) {
- dst_barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
+ if (p_to.has_flag(BARRIER_MASK_VERTEX)) {
+ dst_barrier_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
dst_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
}
+ if (p_to.has_flag(BARRIER_MASK_FRAGMENT)) {
+ dst_barrier_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
+ dst_access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+ }
if (p_to.has_flag(BARRIER_MASK_TRANSFER)) {
dst_barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
dst_access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 24a6d89118..69728133f8 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -3297,7 +3297,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re
track_edits[_get_track_selected()]->release_focus();
}
if (animation.is_valid()) {
- animation->disconnect("changed", callable_mp(this, &AnimationTrackEditor::_animation_changed));
+ animation->disconnect_changed(callable_mp(this, &AnimationTrackEditor::_animation_changed));
_clear_selection();
}
animation = p_anim;
@@ -3308,7 +3308,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re
_update_tracks();
if (animation.is_valid()) {
- animation->connect("changed", callable_mp(this, &AnimationTrackEditor::_animation_changed));
+ animation->connect_changed(callable_mp(this, &AnimationTrackEditor::_animation_changed));
hscroll->show();
edit->set_disabled(read_only);
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 6d345a1d84..8b71d7586f 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -370,7 +370,8 @@ float CreateDialog::_score_type(const String &p_type, const String &p_search) co
// Look through at most 5 recent items
bool in_recent = false;
- for (int i = 0; i < MIN(5, recent->get_item_count()); i++) {
+ constexpr int RECENT_COMPLETION_SIZE = 5;
+ for (int i = 0; i < MIN(RECENT_COMPLETION_SIZE - 1, recent->get_item_count()); i++) {
if (recent->get_item_text(i) == p_type) {
in_recent = true;
break;
@@ -406,7 +407,8 @@ void CreateDialog::_confirmed() {
if (f.is_valid()) {
f->store_line(selected_item);
- for (int i = 0; i < MIN(32, recent->get_item_count()); i++) {
+ constexpr int RECENT_HISTORY_SIZE = 15;
+ for (int i = 0; i < MIN(RECENT_HISTORY_SIZE - 1, recent->get_item_count()); i++) {
if (recent->get_item_text(i) != selected_item) {
f->store_line(recent->get_item_text(i));
}
diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp
index 5e677c454e..e59fc6186a 100644
--- a/editor/debugger/editor_profiler.cpp
+++ b/editor/debugger/editor_profiler.cpp
@@ -33,6 +33,7 @@
#include "core/os/os.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "scene/resources/image_texture.h"
void EditorProfiler::_make_metric_ptrs(Metric &m) {
for (int i = 0; i < m.categories.size(); i++) {
diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h
index eea8ed8365..3f7a0cade5 100644
--- a/editor/debugger/editor_profiler.h
+++ b/editor/debugger/editor_profiler.h
@@ -40,6 +40,8 @@
#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
+class ImageTexture;
+
class EditorProfiler : public VBoxContainer {
GDCLASS(EditorProfiler, VBoxContainer);
diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp
index 2ecb029f1a..984d8e33c5 100644
--- a/editor/debugger/editor_visual_profiler.cpp
+++ b/editor/debugger/editor_visual_profiler.cpp
@@ -33,6 +33,7 @@
#include "core/os/os.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "scene/resources/image_texture.h"
void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) {
++last_metric;
diff --git a/editor/debugger/editor_visual_profiler.h b/editor/debugger/editor_visual_profiler.h
index 5831e3322d..492985506a 100644
--- a/editor/debugger/editor_visual_profiler.h
+++ b/editor/debugger/editor_visual_profiler.h
@@ -41,6 +41,8 @@
#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
+class ImageTexture;
+
class EditorVisualProfiler : public VBoxContainer {
GDCLASS(EditorVisualProfiler, VBoxContainer);
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 71fdce149b..2cbbb59be2 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -45,6 +45,39 @@
#define CONTRIBUTE_URL vformat("%s/contributing/documentation/updating_the_class_reference.html", VERSION_DOCS_URL)
+#ifdef MODULE_MONO_ENABLED
+// Sync with the types mentioned in https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/c_sharp_differences.html
+const Vector<String> classes_with_csharp_differences = {
+ "@GlobalScope",
+ "String",
+ "NodePath",
+ "Signal",
+ "Callable",
+ "RID",
+ "Basis",
+ "Transform2D",
+ "Transform3D",
+ "Rect2",
+ "Rect2i",
+ "AABB",
+ "Quaternion",
+ "Projection",
+ "Color",
+ "Array",
+ "Dictionary",
+ "PackedByteArray",
+ "PackedColorArray",
+ "PackedFloat32Array",
+ "PackedFloat64Array",
+ "PackedInt32Array",
+ "PackedInt64Array",
+ "PackedStringArray",
+ "PackedVector2Array",
+ "PackedVector3Array",
+ "Variant",
+};
+#endif
+
// TODO: this is sometimes used directly as doc->something, other times as EditorHelp::get_doc_data(), which is thread-safe.
// Might this be a problem?
DocTools *EditorHelp::doc = nullptr;
@@ -888,10 +921,26 @@ void EditorHelp::_update_doc() {
class_desc->append_text(TTR("There is currently no description for this class. Please help us by [color=$color][url=$url]contributing one[/url][/color]!").replace("$url", CONTRIBUTE_URL).replace("$color", link_color_text));
}
+ class_desc->add_newline();
+ class_desc->add_newline();
+ }
+
+#ifdef MODULE_MONO_ENABLED
+ if (classes_with_csharp_differences.has(cd.name)) {
+ const String &csharp_differences_url = vformat("%s/tutorials/scripting/c_sharp/c_sharp_differences.html", VERSION_DOCS_URL);
+
+ class_desc->push_color(theme_cache.text_color);
+ _push_normal_font();
+ class_desc->push_indent(1);
+ _add_text("[b]" + TTR("Note:") + "[/b] " + vformat(TTR("There are notable differences when using this API with C#. See [url=%s]C# API differences to GDScript[/url] for more information."), csharp_differences_url));
class_desc->pop();
+ _pop_normal_font();
+ class_desc->pop();
+
class_desc->add_newline();
class_desc->add_newline();
}
+#endif
// Online tutorials
if (cd.tutorials.size()) {
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 367373dc17..d95b1de365 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -45,6 +45,7 @@
#include "scene/gui/texture_rect.h"
#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h"
+#include "scene/resources/style_box_flat.h"
bool EditorInspector::_property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
if (p_property_path.findn(p_filter) != -1) {
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index a5737b7dc6..9a4c4f7f99 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -44,6 +44,7 @@ class OptionButton;
class PanelContainer;
class PopupMenu;
class SpinBox;
+class StyleBoxFlat;
class TextureRect;
class EditorPropertyRevert {
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 0fd9d64602..1bc9f00f08 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -338,13 +338,7 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
} else {
log->add_text(p_message.text);
}
-
- // Need to use pop() to exit out of the RichTextLabels current "push" stack.
- // We only "push" in the above switch when message type != STD and RICH, so only pop when that is the case.
- if (p_message.type != MSG_TYPE_STD && p_message.type != MSG_TYPE_STD_RICH) {
- log->pop();
- }
-
+ log->pop_all(); // Pop all unclosed tags.
log->add_newline();
if (p_replace_previous) {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 358356adf5..5029a12264 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -60,7 +60,9 @@
#include "scene/gui/tab_container.h"
#include "scene/main/window.h"
#include "scene/property_utils.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/packed_scene.h"
+#include "scene/resources/portable_compressed_texture.h"
#include "servers/display_server.h"
#include "servers/navigation_server_3d.h"
#include "servers/physics_server_2d.h"
@@ -2775,6 +2777,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case FILE_QUIT:
case RUN_PROJECT_MANAGER:
case RELOAD_CURRENT_PROJECT: {
+ if (p_confirmed && plugin_to_save) {
+ plugin_to_save->save_external_data();
+ p_confirmed = false;
+ }
+
if (!p_confirmed) {
bool save_each = EDITOR_GET("interface/editor/save_each_scene_on_quit");
if (_next_unsaved_scene(!save_each) == -1) {
@@ -2791,6 +2798,28 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
break;
}
+ plugin_to_save = nullptr;
+ for (int i = 0; i < editor_data.get_editor_plugin_count(); i++) {
+ const String unsaved_status = editor_data.get_editor_plugin(i)->get_unsaved_status();
+ if (!unsaved_status.is_empty()) {
+ if (p_option == RELOAD_CURRENT_PROJECT) {
+ save_confirmation->set_ok_button_text(TTR("Save & Reload"));
+ save_confirmation->set_text(unsaved_status);
+ } else {
+ save_confirmation->set_ok_button_text(TTR("Save & Quit"));
+ save_confirmation->set_text(unsaved_status);
+ }
+ save_confirmation->reset_size();
+ save_confirmation->popup_centered();
+ plugin_to_save = editor_data.get_editor_plugin(i);
+ break;
+ }
+ }
+
+ if (plugin_to_save) {
+ break;
+ }
+
_discard_changes();
break;
}
@@ -3029,13 +3058,21 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) {
if (!editor_data.get_edited_scene_root(i)) {
continue;
}
+
+ String scene_filename = editor_data.get_edited_scene_root(i)->get_scene_file_path();
+ if (p_valid_filename && scene_filename.is_empty()) {
+ continue;
+ }
+
bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(i));
if (unsaved) {
- String scene_filename = editor_data.get_edited_scene_root(i)->get_scene_file_path();
- if (p_valid_filename && scene_filename.is_empty()) {
- continue;
- }
return i;
+ } else {
+ for (int j = 0; j < editor_data.get_editor_plugin_count(); j++) {
+ if (!editor_data.get_editor_plugin(j)->get_unsaved_status(scene_filename).is_empty()) {
+ return i;
+ }
+ }
}
}
return -1;
@@ -3190,7 +3227,7 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed
if (icon.is_valid()) {
tb->set_icon(icon);
// Make sure the control is updated if the icon is reimported.
- icon->connect("changed", callable_mp((Control *)tb, &Control::update_minimum_size));
+ icon->connect_changed(callable_mp((Control *)tb, &Control::update_minimum_size));
} else if (singleton->gui_base->has_theme_icon(p_editor->get_name(), SNAME("EditorIcons"))) {
tb->set_icon(singleton->gui_base->get_theme_icon(p_editor->get_name(), SNAME("EditorIcons")));
}
@@ -3326,6 +3363,11 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled,
return;
}
+ String plugin_version;
+ if (cf->has_section_key("plugin", "version")) {
+ plugin_version = cf->get_value("plugin", "version");
+ }
+
if (!cf->has_section_key("plugin", "script")) {
show_warning(vformat(TTR("Unable to find script field for addon plugin at: '%s'."), addon_path));
return;
@@ -3371,6 +3413,7 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled,
EditorPlugin *ep = memnew(EditorPlugin);
ep->set_script(scr);
+ ep->set_plugin_version(plugin_version);
addon_name_to_plugin[addon_path] = ep;
add_editor_plugin(ep, p_config_changed);
@@ -4407,17 +4450,18 @@ String EditorNode::_get_system_info() const {
const int processor_count = OS::get_singleton()->get_processor_count();
// Prettify
- if (driver_name == "vulkan") {
- driver_name = "Vulkan";
- } else if (driver_name == "opengl3") {
- driver_name = "GLES3";
- }
if (rendering_method == "forward_plus") {
rendering_method = "Forward+";
} else if (rendering_method == "mobile") {
rendering_method = "Mobile";
} else if (rendering_method == "gl_compatibility") {
rendering_method = "Compatibility";
+ driver_name = GLOBAL_GET("rendering/gl_compatibility/driver");
+ }
+ if (driver_name == "vulkan") {
+ driver_name = "Vulkan";
+ } else if (driver_name == "opengl3") {
+ driver_name = "GLES3";
}
// Join info.
@@ -5545,19 +5589,36 @@ void EditorNode::_scene_tab_closed(int p_tab, int p_option) {
return;
}
- bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(p_tab));
- if (unsaved) {
+ String scene_filename = scene->get_scene_file_path();
+ String unsaved_message;
+
+ if (EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(p_tab))) {
+ if (scene_filename.is_empty()) {
+ unsaved_message = TTR("This scene was never saved.");
+ } else {
+ unsaved_message = vformat(TTR("Scene \"%s\" has unsaved changes."), scene_filename);
+ }
+ } else {
+ // Check if any plugin has unsaved changes in that scene.
+ for (int i = 0; i < editor_data.get_editor_plugin_count(); i++) {
+ unsaved_message = editor_data.get_editor_plugin(i)->get_unsaved_status(scene_filename);
+ if (!unsaved_message.is_empty()) {
+ break;
+ }
+ }
+ }
+
+ if (!unsaved_message.is_empty()) {
if (get_current_tab() != p_tab) {
set_current_scene(p_tab);
}
- String scene_filename = scene->get_scene_file_path();
if (current_menu_option == RELOAD_CURRENT_PROJECT) {
save_confirmation->set_ok_button_text(TTR("Save & Reload"));
- save_confirmation->set_text(vformat(TTR("Save changes to '%s' before reloading?"), !scene_filename.is_empty() ? scene_filename : "unsaved scene"));
+ save_confirmation->set_text(unsaved_message + "\n\n" + TTR("Save before reloading?"));
} else {
save_confirmation->set_ok_button_text(TTR("Save & Close"));
- save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), !scene_filename.is_empty() ? scene_filename : "unsaved scene"));
+ save_confirmation->set_text(unsaved_message + "\n\n" + TTR("Save before closing?"));
}
save_confirmation->reset_size();
save_confirmation->popup_centered();
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 65f85a76c9..6384c27c72 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -382,6 +382,7 @@ private:
AcceptDialog *save_accept = nullptr;
EditorAbout *about = nullptr;
AcceptDialog *warning = nullptr;
+ EditorPlugin *plugin_to_save = nullptr;
int overridden_default_layout = -1;
Ref<ConfigFile> default_layout;
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 4232eacd76..2d4c07b263 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -51,6 +51,7 @@
#include "editor/scene_tree_dock.h"
#include "scene/3d/camera_3d.h"
#include "scene/gui/popup_menu.h"
+#include "scene/resources/image_texture.h"
#include "servers/rendering_server.h"
void EditorPlugin::add_custom_type(const String &p_type, const String &p_base, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon) {
@@ -306,6 +307,14 @@ const Ref<Texture2D> EditorPlugin::get_icon() const {
return icon;
}
+String EditorPlugin::get_plugin_version() const {
+ return plugin_version;
+}
+
+void EditorPlugin::set_plugin_version(const String &p_version) {
+ plugin_version = p_version;
+}
+
bool EditorPlugin::has_main_screen() const {
bool success = false;
GDVIRTUAL_CALL(_has_main_screen, success);
@@ -340,7 +349,12 @@ void EditorPlugin::clear() {
GDVIRTUAL_CALL(_clear);
}
-// if editor references external resources/scenes, save them
+String EditorPlugin::get_unsaved_status(const String &p_for_scene) const {
+ String ret;
+ GDVIRTUAL_CALL(_get_unsaved_status, p_for_scene, ret);
+ return ret;
+}
+
void EditorPlugin::save_external_data() {
GDVIRTUAL_CALL(_save_external_data);
}
@@ -577,6 +591,7 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog);
ClassDB::bind_method(D_METHOD("add_debugger_plugin", "script"), &EditorPlugin::add_debugger_plugin);
ClassDB::bind_method(D_METHOD("remove_debugger_plugin", "script"), &EditorPlugin::remove_debugger_plugin);
+ ClassDB::bind_method(D_METHOD("get_plugin_version"), &EditorPlugin::get_plugin_version);
GDVIRTUAL_BIND(_forward_canvas_gui_input, "event");
GDVIRTUAL_BIND(_forward_canvas_draw_over_viewport, "viewport_control");
@@ -593,6 +608,7 @@ void EditorPlugin::_bind_methods() {
GDVIRTUAL_BIND(_get_state);
GDVIRTUAL_BIND(_set_state, "state");
GDVIRTUAL_BIND(_clear);
+ GDVIRTUAL_BIND(_get_unsaved_status, "for_scene");
GDVIRTUAL_BIND(_save_external_data);
GDVIRTUAL_BIND(_apply_changes);
GDVIRTUAL_BIND(_get_breakpoints);
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 69789a4d4f..7dcf62144d 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -61,6 +61,7 @@ class EditorPlugin : public Node {
bool force_draw_over_forwarding_enabled = false;
String last_main_screen_name;
+ String plugin_version;
void _editor_project_settings_changed();
@@ -88,6 +89,7 @@ protected:
GDVIRTUAL0RC(Dictionary, _get_state)
GDVIRTUAL1(_set_state, Dictionary)
GDVIRTUAL0(_clear)
+ GDVIRTUAL1RC(String, _get_unsaved_status, String)
GDVIRTUAL0(_save_external_data)
GDVIRTUAL0(_apply_changes)
GDVIRTUAL0RC(Vector<String>, _get_breakpoints)
@@ -167,6 +169,8 @@ public:
virtual String get_name() const;
virtual const Ref<Texture2D> get_icon() const;
+ virtual String get_plugin_version() const;
+ virtual void set_plugin_version(const String &p_version);
virtual bool has_main_screen() const;
virtual void make_visible(bool p_visible);
virtual void selected_notify() {} //notify that it was raised by the user, not the editor
@@ -175,6 +179,7 @@ public:
virtual Dictionary get_state() const; //save editor state so it can't be reloaded when reloading scene
virtual void set_state(const Dictionary &p_state); //restore editor state (likely was saved with the scene)
virtual void clear(); // clear any temporary data in the editor, reset it (likely new scene or load another scene)
+ virtual String get_unsaved_status(const String &p_for_scene = "") const;
virtual void save_external_data(); // if editor references external resources/scenes, save them
virtual void apply_changes(); // if changes are pending in editor, apply them
virtual void get_breakpoints(List<String> *p_breakpoints);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 875351cacd..77d6ec9ab2 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -772,7 +772,7 @@ void EditorPropertyFlags::setup(const Vector<String> &p_options) {
const int flag_index = flags.size(); // Index of the next element (added by the code below).
// Value for a flag can be explicitly overridden.
- Vector<String> text_split = p_options[i].split(":");
+ Vector<String> text_split = option.split(":");
if (text_split.size() != 1) {
current_val = text_split[1].to_int();
} else {
@@ -782,7 +782,7 @@ void EditorPropertyFlags::setup(const Vector<String> &p_options) {
// Create a CheckBox for the current flag.
CheckBox *cb = memnew(CheckBox);
- cb->set_text(option);
+ cb->set_text(text_split[0]);
cb->set_clip_text(true);
cb->connect("pressed", callable_mp(this, &EditorPropertyFlags::_flag_toggled).bind(flag_index));
add_focusable(cb);
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 8920ad10dc..ea7e1549f5 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -41,6 +41,10 @@
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/scene_tree_dock.h"
+#include "scene/gui/button.h"
+#include "scene/gui/texture_rect.h"
+#include "scene/resources/gradient_texture.h"
+#include "scene/resources/image_texture.h"
void EditorResourcePicker::_update_resource() {
String resource_path;
@@ -218,7 +222,7 @@ void EditorResourcePicker::_update_menu_items() {
edited_resource->get_property_list(&property_list);
bool has_subresources = false;
for (PropertyInfo &p : property_list) {
- if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script")) {
+ if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script") && ((Object *)edited_resource->get(p.name) != nullptr)) {
has_subresources = true;
break;
}
@@ -350,7 +354,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
}
Ref<Resource> unique_resource = edited_resource->duplicate();
- ERR_FAIL_COND(unique_resource.is_null());
+ ERR_FAIL_COND(unique_resource.is_null()); // duplicate() may fail.
edited_resource = unique_resource;
emit_signal(SNAME("resource_changed"), edited_resource);
@@ -362,12 +366,30 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
return;
}
- Ref<Resource> unique_resource = edited_resource->duplicate(true);
- ERR_FAIL_COND(unique_resource.is_null());
+ if (!duplicate_resources_dialog) {
+ duplicate_resources_dialog = memnew(ConfirmationDialog);
+ add_child(duplicate_resources_dialog);
+ duplicate_resources_dialog->set_title(TTR("Make Unique (Recursive)"));
+ duplicate_resources_dialog->connect("confirmed", callable_mp(this, &EditorResourcePicker::_duplicate_selected_resources));
- edited_resource = unique_resource;
- emit_signal(SNAME("resource_changed"), edited_resource);
- _update_resource();
+ VBoxContainer *vb = memnew(VBoxContainer);
+ duplicate_resources_dialog->add_child(vb);
+
+ Label *label = memnew(Label(TTR("Select resources to make unique:")));
+ vb->add_child(label);
+
+ duplicate_resources_tree = memnew(Tree);
+ vb->add_child(duplicate_resources_tree);
+ duplicate_resources_tree->set_columns(2);
+ duplicate_resources_tree->set_v_size_flags(SIZE_EXPAND_FILL);
+ }
+
+ duplicate_resources_tree->clear();
+ TreeItem *root = duplicate_resources_tree->create_item();
+ _gather_resources_to_duplicate(edited_resource, root);
+
+ duplicate_resources_dialog->reset_size();
+ duplicate_resources_dialog->popup_centered(Vector2(500, 400) * EDSCALE);
} break;
case OBJ_MENU_SAVE: {
@@ -808,6 +830,11 @@ void EditorResourcePicker::_notification(int p_what) {
}
}
+void EditorResourcePicker::set_assign_button_min_size(const Size2i &p_size) {
+ assign_button_min_size = p_size;
+ assign_button->set_custom_minimum_size(assign_button_min_size);
+}
+
void EditorResourcePicker::set_base_type(const String &p_base_type) {
base_type = p_base_type;
@@ -920,6 +947,89 @@ void EditorResourcePicker::_ensure_resource_menu() {
edit_menu->connect("popup_hide", callable_mp((BaseButton *)edit_button, &BaseButton::set_pressed).bind(false));
}
+void EditorResourcePicker::_gather_resources_to_duplicate(const Ref<Resource> p_resource, TreeItem *p_item, const String &p_property_name) const {
+ p_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+
+ String res_name = p_resource->get_name();
+ if (res_name.is_empty() && !p_resource->is_built_in()) {
+ res_name = p_resource->get_path().get_file();
+ }
+
+ if (res_name.is_empty()) {
+ p_item->set_text(0, p_resource->get_class());
+ } else {
+ p_item->set_text(0, vformat("%s (%s)", p_resource->get_class(), res_name));
+ }
+
+ p_item->set_icon(0, EditorNode::get_singleton()->get_object_icon(p_resource.ptr()));
+ p_item->set_editable(0, true);
+
+ Array meta;
+ meta.append(p_resource);
+ p_item->set_metadata(0, meta);
+
+ if (!p_property_name.is_empty()) {
+ p_item->set_text(1, p_property_name);
+ }
+
+ static Vector<String> unique_exceptions = { "Image", "Shader", "Mesh", "FontFile" };
+ if (!unique_exceptions.has(p_resource->get_class())) {
+ // Automatically select resource, unless it's something that shouldn't be duplicated.
+ p_item->set_checked(0, true);
+ }
+
+ List<PropertyInfo> plist;
+ p_resource->get_property_list(&plist);
+
+ for (const PropertyInfo &E : plist) {
+ if (!(E.usage & PROPERTY_USAGE_STORAGE) || E.type != Variant::OBJECT || E.hint != PROPERTY_HINT_RESOURCE_TYPE) {
+ continue;
+ }
+
+ Ref<Resource> res = p_resource->get(E.name);
+ if (res.is_null()) {
+ continue;
+ }
+
+ TreeItem *child = p_item->create_child();
+ _gather_resources_to_duplicate(res, child, E.name);
+
+ meta = child->get_metadata(0);
+ // Remember property name.
+ meta.append(E.name);
+
+ if ((E.usage & PROPERTY_USAGE_NEVER_DUPLICATE)) {
+ // The resource can't be duplicated, but make it appear on the list anyway.
+ child->set_checked(0, false);
+ child->set_editable(0, false);
+ }
+ }
+}
+
+void EditorResourcePicker::_duplicate_selected_resources() {
+ for (TreeItem *item = duplicate_resources_tree->get_root(); item; item = item->get_next_in_tree()) {
+ if (!item->is_checked(0)) {
+ continue;
+ }
+
+ Array meta = item->get_metadata(0);
+ Ref<Resource> res = meta[0];
+ Ref<Resource> unique_resource = res->duplicate();
+ ERR_FAIL_COND(unique_resource.is_null()); // duplicate() may fail.
+ meta[0] = unique_resource;
+
+ if (meta.size() == 1) { // Root.
+ edited_resource = unique_resource;
+ emit_signal(SNAME("resource_changed"), edited_resource);
+ _update_resource();
+ } else {
+ Array parent_meta = item->get_parent()->get_metadata(0);
+ Ref<Resource> parent = parent_meta[0];
+ parent->set(meta[1], unique_resource);
+ }
+ }
+}
+
EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) {
assign_button = memnew(Button);
assign_button->set_flat(true);
diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h
index a302e24957..856ef974d3 100644
--- a/editor/editor_resource_picker.h
+++ b/editor/editor_resource_picker.h
@@ -32,12 +32,15 @@
#define EDITOR_RESOURCE_PICKER_H
#include "scene/gui/box_container.h"
-#include "scene/gui/button.h"
-#include "scene/gui/popup_menu.h"
-#include "scene/gui/texture_rect.h"
+class Button;
+class ConfirmationDialog;
class EditorFileDialog;
class EditorQuickOpen;
+class PopupMenu;
+class TextureRect;
+class Tree;
+class TreeItem;
class EditorResourcePicker : public HBoxContainer {
GDCLASS(EditorResourcePicker, HBoxContainer);
@@ -56,6 +59,9 @@ class EditorResourcePicker : public HBoxContainer {
EditorFileDialog *file_dialog = nullptr;
EditorQuickOpen *quick_open = nullptr;
+ ConfirmationDialog *duplicate_resources_dialog = nullptr;
+ Tree *duplicate_resources_tree = nullptr;
+
Size2i assign_button_min_size = Size2i(1, 1);
enum MenuOption {
@@ -99,6 +105,8 @@ class EditorResourcePicker : public HBoxContainer {
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
void _ensure_resource_menu();
+ void _gather_resources_to_duplicate(const Ref<Resource> p_resource, TreeItem *p_item, const String &p_property_name = "") const;
+ void _duplicate_selected_resources();
protected:
virtual void _update_resource();
@@ -107,10 +115,7 @@ protected:
static void _bind_methods();
void _notification(int p_what);
- void set_assign_button_min_size(const Size2i &p_size) {
- assign_button_min_size = p_size;
- assign_button->set_custom_minimum_size(assign_button_min_size);
- }
+ void set_assign_button_min_size(const Size2i &p_size);
GDVIRTUAL1(_set_create_options, Object *)
GDVIRTUAL1R(bool, _handle_menu_selected, int)
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index f5da9da8e7..38a78babfb 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -40,6 +40,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "scene/resources/image_texture.h"
bool EditorResourcePreviewGenerator::handles(const String &p_type) const {
bool success = false;
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index 84835094bb..925039139b 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -35,7 +35,9 @@
#include "core/os/thread.h"
#include "core/templates/safe_refcount.h"
#include "scene/main/node.h"
-#include "scene/resources/texture.h"
+
+class ImageTexture;
+class Texture2D;
class EditorResourcePreviewGenerator : public RefCounted {
GDCLASS(EditorResourcePreviewGenerator, RefCounted);
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index beccf0f2ec..cf6a8f1368 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
#include "editor/export/editor_export_platform.h"
+#include "scene/resources/image_texture.h"
void EditorRunNative::_notification(int p_what) {
switch (p_what) {
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 537e862a51..4ae0aa7e4a 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -688,6 +688,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/2d/bone_outline_color", Color(0.35, 0.35, 0.35, 0.5));
_initial_set("editors/2d/bone_outline_size", 2);
_initial_set("editors/2d/viewport_border_color", Color(0.4, 0.4, 1.0, 0.4));
+ _initial_set("editors/2d/use_integer_zoom_by_default", false);
// Panning
// Enum should be in sync with ControlScheme in ViewPanner.
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 1c56c23ee1..99feee42b8 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -36,6 +36,10 @@
#include "editor/editor_icons.gen.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "scene/resources/image_texture.h"
+#include "scene/resources/style_box_flat.h"
+#include "scene/resources/style_box_line.h"
+#include "scene/resources/style_box_texture.h"
#include "modules/modules_enabled.gen.h" // For svg.
#ifdef MODULE_SVG_ENABLED
@@ -1605,6 +1609,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_window->set_border_width(SIDE_TOP, 24 * EDSCALE);
style_window->set_expand_margin(SIDE_TOP, 24 * EDSCALE);
theme->set_stylebox("embedded_border", "Window", style_window);
+ theme->set_stylebox("embedded_unfocused_border", "Window", style_window);
theme->set_color("title_color", "Window", font_color);
theme->set_icon("close", "Window", theme->get_icon(SNAME("GuiClose"), SNAME("EditorIcons")));
diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp
index fd2d51be32..abfbd5e7c0 100644
--- a/editor/editor_undo_redo_manager.cpp
+++ b/editor/editor_undo_redo_manager.cpp
@@ -264,6 +264,7 @@ void EditorUndoRedoManager::commit_action(bool p_execute) {
pending_action.action_name == prev_action.action_name && pending_action.action_name == pre_prev_action.action_name) {
pending_action = Action();
is_committing = false;
+ emit_signal(SNAME("history_changed"));
return;
}
} break;
@@ -272,6 +273,7 @@ void EditorUndoRedoManager::commit_action(bool p_execute) {
if (pending_action.merge_mode == prev_action.merge_mode && pending_action.action_name == prev_action.action_name) {
pending_action = Action();
is_committing = false;
+ emit_signal(SNAME("history_changed"));
return;
}
} break;
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index d04eeafd07..5ee9b187a2 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -45,6 +45,7 @@
#include "editor/export/editor_export.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor_export_plugin.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/packed_scene.h"
static int _get_pad(int p_alignment, int p_n) {
@@ -989,7 +990,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
struct SortByName {
bool operator()(const Ref<EditorExportPlugin> &left, const Ref<EditorExportPlugin> &right) const {
- return left->_get_name() < right->_get_name();
+ return left->get_name() < right->get_name();
}
};
@@ -1032,14 +1033,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
if (export_plugins.write[i]->_begin_customize_resources(Ref<EditorExportPlatform>(this), features_psa)) {
customize_resources_plugins.push_back(export_plugins[i]);
- custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->_get_name().hash64(), custom_resources_hash);
+ custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->get_name().hash64(), custom_resources_hash);
uint64_t hash = export_plugins[i]->_get_customization_configuration_hash();
custom_resources_hash = hash_murmur3_one_64(hash, custom_resources_hash);
}
if (export_plugins.write[i]->_begin_customize_scenes(Ref<EditorExportPlatform>(this), features_psa)) {
customize_scenes_plugins.push_back(export_plugins[i]);
- custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->_get_name().hash64(), custom_resources_hash);
+ custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->get_name().hash64(), custom_resources_hash);
uint64_t hash = export_plugins[i]->_get_customization_configuration_hash();
custom_scene_hash = hash_murmur3_one_64(hash, custom_scene_hash);
}
@@ -1799,6 +1800,24 @@ bool EditorExportPlatform::can_export(const Ref<EditorExportPreset> &p_preset, S
if (!templates_error.is_empty()) {
r_error += templates_error;
}
+
+ String export_plugins_warning;
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ for (int i = 0; i < export_plugins.size(); i++) {
+ Ref<EditorExportPlatform> export_platform = Ref<EditorExportPlatform>(this);
+ if (!export_plugins[i]->supports_platform(export_platform)) {
+ continue;
+ }
+
+ String plugin_warning = export_plugins.write[i]->_has_valid_export_configuration(export_platform, p_preset);
+ if (!plugin_warning.is_empty()) {
+ export_plugins_warning += plugin_warning;
+ }
+ }
+
+ if (!export_plugins_warning.is_empty()) {
+ r_error += export_plugins_warning;
+ }
#endif
String project_configuration_error;
diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h
index 121e00ccae..763836e3ec 100644
--- a/editor/export/editor_export_platform.h
+++ b/editor/export/editor_export_platform.h
@@ -40,6 +40,7 @@ struct EditorProgress;
#include "editor_export_shared_object.h"
#include "scene/gui/rich_text_label.h"
#include "scene/main/node.h"
+#include "scene/resources/image_texture.h"
class EditorExportPlugin;
diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp
index df1026d0ed..ec34ffd1df 100644
--- a/editor/export/editor_export_platform_pc.cpp
+++ b/editor/export/editor_export_platform_pc.cpp
@@ -31,6 +31,7 @@
#include "editor_export_platform_pc.h"
#include "core/config/project_settings.h"
+#include "scene/resources/image_texture.h"
void EditorExportPlatformPC::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
if (p_preset->get("texture_format/bptc")) {
diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp
index 4e2c1a9af7..6576960b9a 100644
--- a/editor/export/editor_export_plugin.cpp
+++ b/editor/export/editor_export_plugin.cpp
@@ -132,6 +132,27 @@ Variant EditorExportPlugin::get_option(const StringName &p_name) const {
return export_preset->get(p_name);
}
+String EditorExportPlugin::_has_valid_export_configuration(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset) {
+ String warning;
+ if (!supports_platform(p_export_platform)) {
+ warning += vformat(TTR("Plugin \"%s\" is not supported on \"%s\""), get_name(), p_export_platform->get_name());
+ warning += "\n";
+ return warning;
+ }
+
+ set_export_preset(p_preset);
+ List<EditorExportPlatform::ExportOption> options;
+ _get_export_options(p_export_platform, &options);
+ for (const EditorExportPlatform::ExportOption &E : options) {
+ String option_warning = _get_export_option_warning(p_export_platform, E.option.name);
+ if (!option_warning.is_empty()) {
+ warning += option_warning + "\n";
+ }
+ }
+
+ return warning;
+}
+
void EditorExportPlugin::_export_file_script(const String &p_path, const String &p_type, const Vector<String> &p_features) {
GDVIRTUAL_CALL(_export_file, p_path, p_type, p_features);
}
@@ -184,12 +205,54 @@ void EditorExportPlugin::_end_customize_resources() {
GDVIRTUAL_CALL(_end_customize_resources);
}
-String EditorExportPlugin::_get_name() const {
+String EditorExportPlugin::get_name() const {
String ret;
GDVIRTUAL_REQUIRED_CALL(_get_name, ret);
return ret;
}
+bool EditorExportPlugin::supports_platform(const Ref<EditorExportPlatform> &p_export_platform) const {
+ bool ret = false;
+ GDVIRTUAL_CALL(_supports_platform, p_export_platform, ret);
+ return ret;
+}
+
+PackedStringArray EditorExportPlugin::get_android_dependencies(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
+ PackedStringArray ret;
+ GDVIRTUAL_CALL(_get_android_dependencies, p_export_platform, p_debug, ret);
+ return ret;
+}
+
+PackedStringArray EditorExportPlugin::get_android_dependencies_maven_repos(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
+ PackedStringArray ret;
+ GDVIRTUAL_CALL(_get_android_dependencies_maven_repos, p_export_platform, p_debug, ret);
+ return ret;
+}
+
+PackedStringArray EditorExportPlugin::get_android_libraries(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
+ PackedStringArray ret;
+ GDVIRTUAL_CALL(_get_android_libraries, p_export_platform, p_debug, ret);
+ return ret;
+}
+
+String EditorExportPlugin::get_android_manifest_activity_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
+ String ret;
+ GDVIRTUAL_CALL(_get_android_manifest_activity_element_contents, p_export_platform, p_debug, ret);
+ return ret;
+}
+
+String EditorExportPlugin::get_android_manifest_application_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
+ String ret;
+ GDVIRTUAL_CALL(_get_android_manifest_application_element_contents, p_export_platform, p_debug, ret);
+ return ret;
+}
+
+String EditorExportPlugin::get_android_manifest_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const {
+ String ret;
+ GDVIRTUAL_CALL(_get_android_manifest_element_contents, p_export_platform, p_debug, ret);
+ return ret;
+}
+
PackedStringArray EditorExportPlugin::_get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const {
PackedStringArray ret;
GDVIRTUAL_CALL(_get_export_features, p_platform, p_debug, ret);
@@ -216,6 +279,12 @@ bool EditorExportPlugin::_should_update_export_options(const Ref<EditorExportPla
return ret;
}
+String EditorExportPlugin::_get_export_option_warning(const Ref<EditorExportPlatform> &p_export_platform, const String &p_option_name) const {
+ String ret;
+ GDVIRTUAL_CALL(_get_export_option_warning, p_export_platform, p_option_name, ret);
+ return ret;
+}
+
void EditorExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) {
}
@@ -257,9 +326,19 @@ void EditorExportPlugin::_bind_methods() {
GDVIRTUAL_BIND(_get_export_options, "platform");
GDVIRTUAL_BIND(_should_update_export_options, "platform");
+ GDVIRTUAL_BIND(_get_export_option_warning, "platform", "option");
GDVIRTUAL_BIND(_get_export_features, "platform", "debug");
GDVIRTUAL_BIND(_get_name);
+
+ GDVIRTUAL_BIND(_supports_platform, "platform");
+
+ GDVIRTUAL_BIND(_get_android_dependencies, "platform", "debug");
+ GDVIRTUAL_BIND(_get_android_dependencies_maven_repos, "platform", "debug");
+ GDVIRTUAL_BIND(_get_android_libraries, "platform", "debug");
+ GDVIRTUAL_BIND(_get_android_manifest_activity_element_contents, "platform", "debug");
+ GDVIRTUAL_BIND(_get_android_manifest_application_element_contents, "platform", "debug");
+ GDVIRTUAL_BIND(_get_android_manifest_element_contents, "platform", "debug");
}
EditorExportPlugin::EditorExportPlugin() {
diff --git a/editor/export/editor_export_plugin.h b/editor/export/editor_export_plugin.h
index 120141b347..7d866ce37e 100644
--- a/editor/export/editor_export_plugin.h
+++ b/editor/export/editor_export_plugin.h
@@ -42,6 +42,7 @@ class EditorExportPlugin : public RefCounted {
friend class EditorExport;
friend class EditorExportPlatform;
+ friend class EditorExportPreset;
Ref<EditorExportPreset> export_preset;
@@ -85,6 +86,8 @@ class EditorExportPlugin : public RefCounted {
void _export_begin_script(const Vector<String> &p_features, bool p_debug, const String &p_path, int p_flags);
void _export_end_script();
+ String _has_valid_export_configuration(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset);
+
protected:
void set_export_preset(const Ref<EditorExportPreset> &p_preset);
Ref<EditorExportPreset> get_export_preset() const;
@@ -125,9 +128,19 @@ protected:
GDVIRTUAL2RC(PackedStringArray, _get_export_features, const Ref<EditorExportPlatform> &, bool);
GDVIRTUAL1RC(TypedArray<Dictionary>, _get_export_options, const Ref<EditorExportPlatform> &);
GDVIRTUAL1RC(bool, _should_update_export_options, const Ref<EditorExportPlatform> &);
+ GDVIRTUAL2RC(String, _get_export_option_warning, const Ref<EditorExportPlatform> &, String);
GDVIRTUAL0RC(String, _get_name)
+ GDVIRTUAL1RC(bool, _supports_platform, const Ref<EditorExportPlatform> &);
+
+ GDVIRTUAL2RC(PackedStringArray, _get_android_dependencies, const Ref<EditorExportPlatform> &, bool);
+ GDVIRTUAL2RC(PackedStringArray, _get_android_dependencies_maven_repos, const Ref<EditorExportPlatform> &, bool);
+ GDVIRTUAL2RC(PackedStringArray, _get_android_libraries, const Ref<EditorExportPlatform> &, bool);
+ GDVIRTUAL2RC(String, _get_android_manifest_activity_element_contents, const Ref<EditorExportPlatform> &, bool);
+ GDVIRTUAL2RC(String, _get_android_manifest_application_element_contents, const Ref<EditorExportPlatform> &, bool);
+ GDVIRTUAL2RC(String, _get_android_manifest_element_contents, const Ref<EditorExportPlatform> &, bool);
+
virtual bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features); // Return true if this plugin does property export customization
virtual Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path); // If nothing is returned, it means do not touch (nothing changed). If something is returned (either the same or a different resource) it means changes are made.
@@ -142,10 +155,20 @@ protected:
virtual PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
virtual void _get_export_options(const Ref<EditorExportPlatform> &p_export_platform, List<EditorExportPlatform::ExportOption> *r_options) const;
virtual bool _should_update_export_options(const Ref<EditorExportPlatform> &p_export_platform) const;
-
- virtual String _get_name() const;
+ virtual String _get_export_option_warning(const Ref<EditorExportPlatform> &p_export_platform, const String &p_option_name) const;
public:
+ virtual String get_name() const;
+
+ virtual bool supports_platform(const Ref<EditorExportPlatform> &p_export_platform) const;
+
+ virtual PackedStringArray get_android_dependencies(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
+ virtual PackedStringArray get_android_dependencies_maven_repos(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
+ virtual PackedStringArray get_android_libraries(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
+ virtual String get_android_manifest_activity_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
+ virtual String get_android_manifest_application_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
+ virtual String get_android_manifest_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
+
Vector<String> get_ios_frameworks() const;
Vector<String> get_ios_embedded_frameworks() const;
Vector<String> get_ios_project_static_libs() const;
diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp
index a7dc44e3a8..dfc0c23afc 100644
--- a/editor/export/editor_export_preset.cpp
+++ b/editor/export/editor_export_preset.cpp
@@ -57,7 +57,26 @@ void EditorExportPreset::_bind_methods() {
}
String EditorExportPreset::_get_property_warning(const StringName &p_name) const {
- return platform->get_export_option_warning(this, p_name);
+ String warning = platform->get_export_option_warning(this, p_name);
+ if (!warning.is_empty()) {
+ warning += "\n";
+ }
+
+ // Get property warning from editor export plugins.
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ for (int i = 0; i < export_plugins.size(); i++) {
+ if (!export_plugins[i]->supports_platform(platform)) {
+ continue;
+ }
+
+ export_plugins.write[i]->set_export_preset(Ref<EditorExportPreset>(this));
+ String plugin_warning = export_plugins[i]->_get_export_option_warning(platform, p_name);
+ if (!plugin_warning.is_empty()) {
+ warning += plugin_warning + "\n";
+ }
+ }
+
+ return warning;
}
void EditorExportPreset::_get_property_list(List<PropertyInfo> *p_list) const {
diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp
index e551b0531a..42e4b6f6f6 100644
--- a/editor/export/export_template_manager.cpp
+++ b/editor/export/export_template_manager.cpp
@@ -669,11 +669,8 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_
f->store_line(VERSION_FULL_CONFIG);
}
- // Create the android plugins directory.
- Error err = da->make_dir_recursive("android/plugins");
- ERR_FAIL_COND_V(err != OK, err);
-
- err = da->make_dir_recursive("android/build");
+ // Create the android build directory.
+ Error err = da->make_dir_recursive("android/build");
ERR_FAIL_COND_V(err != OK, err);
{
// Add an empty .gdignore file to avoid scan.
diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp
index 866a6db2a6..f928a0fd30 100644
--- a/editor/gui/editor_toaster.cpp
+++ b/editor/gui/editor_toaster.cpp
@@ -35,6 +35,7 @@
#include "scene/gui/button.h"
#include "scene/gui/label.h"
#include "scene/gui/panel_container.h"
+#include "scene/resources/style_box_flat.h"
EditorToaster *EditorToaster::singleton = nullptr;
diff --git a/editor/gui/editor_toaster.h b/editor/gui/editor_toaster.h
index 4837756b4e..3e39d9d519 100644
--- a/editor/gui/editor_toaster.h
+++ b/editor/gui/editor_toaster.h
@@ -37,6 +37,7 @@
class Button;
class PanelContainer;
+class StyleBoxFlat;
class EditorToaster : public HBoxContainer {
GDCLASS(EditorToaster, HBoxContainer);
diff --git a/editor/import/audio_stream_import_settings.cpp b/editor/import/audio_stream_import_settings.cpp
index abf56c7ef8..1f7fbd8c59 100644
--- a/editor/import/audio_stream_import_settings.cpp
+++ b/editor/import/audio_stream_import_settings.cpp
@@ -395,7 +395,7 @@ void AudioStreamImportSettings::_seek_to(real_t p_x) {
void AudioStreamImportSettings::edit(const String &p_path, const String &p_importer, const Ref<AudioStream> &p_stream) {
if (!stream.is_null()) {
- stream->disconnect("changed", callable_mp(this, &AudioStreamImportSettings::_audio_changed));
+ stream->disconnect_changed(callable_mp(this, &AudioStreamImportSettings::_audio_changed));
}
importer = p_importer;
@@ -408,7 +408,7 @@ void AudioStreamImportSettings::edit(const String &p_path, const String &p_impor
_duration_label->set_text(text);
if (!stream.is_null()) {
- stream->connect("changed", callable_mp(this, &AudioStreamImportSettings::_audio_changed));
+ stream->connect_changed(callable_mp(this, &AudioStreamImportSettings::_audio_changed));
_preview->queue_redraw();
_indicator->queue_redraw();
color_rect->queue_redraw();
diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index 89a0f4ca3c..3c27864eff 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_node.h"
#include "editor/import/resource_importer_texture.h"
#include "editor/import/resource_importer_texture_settings.h"
+#include "scene/resources/compressed_texture.h"
#include "scene/resources/texture.h"
String ResourceImporterLayeredTexture::get_importer_name() const {
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index e81e836e9e..92367eab8b 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/import/resource_importer_texture_settings.h"
+#include "scene/resources/compressed_texture.h"
void ResourceImporterTexture::_texture_reimport_roughness(const Ref<CompressedTexture2D> &p_tex, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_channel) {
ERR_FAIL_COND(p_tex.is_null());
diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp
index 5a85160690..d437f23740 100644
--- a/editor/import/resource_importer_texture_atlas.cpp
+++ b/editor/import/resource_importer_texture_atlas.cpp
@@ -36,8 +36,10 @@
#include "core/io/resource_saver.h"
#include "core/math/geometry_2d.h"
#include "editor/editor_atlas_packer.h"
+#include "scene/resources/atlas_texture.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/mesh.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/mesh_texture.h"
String ResourceImporterTextureAtlas::get_importer_name() const {
return "texture_atlas";
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index ea210872f7..1abf9d57ce 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -48,6 +48,7 @@
#include "scene/gui/separator.h"
#include "scene/gui/view_panner.h"
#include "scene/main/window.h"
+#include "scene/resources/style_box_flat.h"
void AnimationNodeBlendTreeEditor::add_custom_type(const String &p_name, const Ref<Script> &p_script) {
for (int i = 0; i < add_options.size(); i++) {
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 1fdb1d4a6e..7f4e7460f8 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -47,6 +47,7 @@
#include "scene/gui/separator.h"
#include "scene/main/window.h"
#include "scene/resources/animation.h"
+#include "scene/resources/image_texture.h"
#include "scene/scene_string_names.h"
#include "servers/rendering_server.h"
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index 327200506f..8c46b5c36e 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -42,6 +42,7 @@
#include "scene/gui/tree.h"
class AnimationPlayerEditorPlugin;
+class ImageTexture;
class AnimationPlayerEditor : public VBoxContainer {
GDCLASS(AnimationPlayerEditor, VBoxContainer);
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index 49f073f245..54296411f7 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -50,6 +50,7 @@
#include "scene/gui/tree.h"
#include "scene/main/viewport.h"
#include "scene/main/window.h"
+#include "scene/resources/style_box_flat.h"
#include "scene/scene_string_names.h"
bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) {
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index b2e40fa6c0..5c26199af1 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -42,6 +42,7 @@
#include "editor/gui/editor_file_dialog.h"
#include "editor/project_settings_editor.h"
#include "scene/gui/menu_button.h"
+#include "scene/resources/image_texture.h"
static inline void setup_http_request(HTTPRequest *request) {
request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true));
diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp
index e01849ff26..89579150c2 100644
--- a/editor/plugins/audio_stream_editor_plugin.cpp
+++ b/editor/plugins/audio_stream_editor_plugin.cpp
@@ -30,7 +30,6 @@
#include "audio_stream_editor_plugin.h"
-#include "core/core_string_names.h"
#include "editor/audio_stream_preview.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
@@ -195,7 +194,7 @@ void AudioStreamEditor::_seek_to(real_t p_x) {
void AudioStreamEditor::set_stream(const Ref<AudioStream> &p_stream) {
if (stream.is_valid()) {
- stream->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &AudioStreamEditor::_stream_changed));
+ stream->disconnect_changed(callable_mp(this, &AudioStreamEditor::_stream_changed));
}
stream = p_stream;
@@ -203,7 +202,7 @@ void AudioStreamEditor::set_stream(const Ref<AudioStream> &p_stream) {
hide();
return;
}
- stream->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &AudioStreamEditor::_stream_changed));
+ stream->connect_changed(callable_mp(this, &AudioStreamEditor::_stream_changed));
_player->set_stream(stream);
_current = 0;
diff --git a/editor/plugins/bit_map_editor_plugin.cpp b/editor/plugins/bit_map_editor_plugin.cpp
index 30fc60b0e0..3388cab006 100644
--- a/editor/plugins/bit_map_editor_plugin.cpp
+++ b/editor/plugins/bit_map_editor_plugin.cpp
@@ -33,6 +33,7 @@
#include "editor/editor_scale.h"
#include "scene/gui/label.h"
#include "scene/gui/texture_rect.h"
+#include "scene/resources/image_texture.h"
void BitMapEditor::setup(const Ref<BitMap> &p_bitmap) {
texture_rect->set_texture(ImageTexture::create_from_image(p_bitmap->convert_to_image()));
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 937a0ba6ba..0386a05cc2 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -62,6 +62,7 @@
#include "scene/main/canvas_layer.h"
#include "scene/main/window.h"
#include "scene/resources/packed_scene.h"
+#include "scene/resources/style_box_texture.h"
// Min and Max are power of two in order to play nicely with successive increment.
// That way, we can naturally reach a 100% zoom from boundaries.
@@ -1290,7 +1291,13 @@ void CanvasItemEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref
if (mb.is_valid()) {
// Special behvior for scroll events, as the zoom_by_increment method can smartly end up on powers of two.
int increment = p_zoom_factor > 1.0 ? 1 : -1;
- zoom_widget->set_zoom_by_increments(increment, mb->is_alt_pressed());
+ bool by_integer = mb->is_alt_pressed();
+
+ if (EDITOR_GET("editors/2d/use_integer_zoom_by_default")) {
+ by_integer = !by_integer;
+ }
+
+ zoom_widget->set_zoom_by_increments(increment, by_integer);
} else {
zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor);
}
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 4e2a629c7e..2f97cda343 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -39,12 +39,13 @@ class AcceptDialog;
class CanvasItemEditorViewport;
class ConfirmationDialog;
class EditorData;
-class EditorZoomWidget;
class EditorSelection;
+class EditorZoomWidget;
class HScrollBar;
class HSplitContainer;
class MenuButton;
class PanelContainer;
+class StyleBoxTexture;
class ViewPanner;
class VScrollBar;
class VSplitContainer;
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index a1a692bdd1..3f5dae1423 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -31,7 +31,6 @@
#include "curve_editor_plugin.h"
#include "canvas_item_editor_plugin.h"
-#include "core/core_string_names.h"
#include "core/input/input.h"
#include "core/math/geometry_2d.h"
#include "core/os/keyboard.h"
@@ -45,6 +44,7 @@
#include "scene/gui/menu_button.h"
#include "scene/gui/popup_menu.h"
#include "scene/gui/separator.h"
+#include "scene/resources/image_texture.h"
CurveEdit::CurveEdit() {
set_focus_mode(FOCUS_ALL);
@@ -61,14 +61,14 @@ void CurveEdit::set_curve(Ref<Curve> p_curve) {
}
if (curve.is_valid()) {
- curve->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveEdit::_curve_changed));
+ curve->disconnect_changed(callable_mp(this, &CurveEdit::_curve_changed));
curve->disconnect(Curve::SIGNAL_RANGE_CHANGED, callable_mp(this, &CurveEdit::_curve_changed));
}
curve = p_curve;
if (curve.is_valid()) {
- curve->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveEdit::_curve_changed));
+ curve->connect_changed(callable_mp(this, &CurveEdit::_curve_changed));
curve->connect(Curve::SIGNAL_RANGE_CHANGED, callable_mp(this, &CurveEdit::_curve_changed));
}
diff --git a/editor/plugins/dedicated_server_export_plugin.h b/editor/plugins/dedicated_server_export_plugin.h
index cb014ae52d..8991c052b3 100644
--- a/editor/plugins/dedicated_server_export_plugin.h
+++ b/editor/plugins/dedicated_server_export_plugin.h
@@ -40,7 +40,7 @@ private:
EditorExportPreset::FileExportMode _get_export_mode_for_path(const String &p_path);
protected:
- String _get_name() const override { return "DedicatedServer"; }
+ String get_name() const override { return "DedicatedServer"; }
PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const override;
uint64_t _get_customization_configuration_hash() const override;
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 2b0691b36f..fba45e5372 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -37,8 +37,11 @@
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "scene/resources/atlas_texture.h"
#include "scene/resources/bit_map.h"
#include "scene/resources/font.h"
+#include "scene/resources/gradient_texture.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
#include "servers/audio/audio_stream.h"
diff --git a/editor/plugins/gdextension_export_plugin.h b/editor/plugins/gdextension_export_plugin.h
index d1c47ab14e..54e6899796 100644
--- a/editor/plugins/gdextension_export_plugin.h
+++ b/editor/plugins/gdextension_export_plugin.h
@@ -36,7 +36,7 @@
class GDExtensionExportPlugin : public EditorExportPlugin {
protected:
virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features);
- virtual String _get_name() const { return "GDExtension"; }
+ virtual String get_name() const { return "GDExtension"; }
};
void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) {
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index 8631ee05c8..c84475982d 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -39,6 +39,7 @@
#include "scene/2d/cpu_particles_2d.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/separator.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/particle_process_material.h"
void GPUParticles2DEditorPlugin::edit(Object *p_object) {
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index 65f66c2661..f0b2e32c72 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -38,6 +38,7 @@
#include "scene/3d/cpu_particles_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/gui/menu_button.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/particle_process_material.h"
bool GPUParticles3DEditorBase::_generate(Vector<Vector3> &points, Vector<Vector3> &normals) {
diff --git a/editor/plugins/gradient_editor.cpp b/editor/plugins/gradient_editor.cpp
index 000db06d48..59bd0f02fc 100644
--- a/editor/plugins/gradient_editor.cpp
+++ b/editor/plugins/gradient_editor.cpp
@@ -34,11 +34,12 @@
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_undo_redo_manager.h"
+#include "scene/resources/gradient_texture.h"
void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) {
gradient = p_gradient;
connect("ramp_changed", callable_mp(this, &GradientEditor::_ramp_changed));
- gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed));
+ gradient->connect_changed(callable_mp(this, &GradientEditor::_gradient_changed));
set_points(gradient->get_points());
set_interpolation_mode(gradient->get_interpolation_mode());
set_interpolation_color_space(gradient->get_interpolation_color_space());
diff --git a/editor/plugins/gradient_editor.h b/editor/plugins/gradient_editor.h
index b78b740f4f..9ff39b2213 100644
--- a/editor/plugins/gradient_editor.h
+++ b/editor/plugins/gradient_editor.h
@@ -35,6 +35,8 @@
#include "scene/gui/popup.h"
#include "scene/resources/gradient.h"
+class GradientTexture1D;
+
class GradientEditor : public Control {
GDCLASS(GradientEditor, Control);
diff --git a/editor/plugins/gradient_texture_2d_editor_plugin.cpp b/editor/plugins/gradient_texture_2d_editor_plugin.cpp
index 08de48af18..c48de7c3dd 100644
--- a/editor/plugins/gradient_texture_2d_editor_plugin.cpp
+++ b/editor/plugins/gradient_texture_2d_editor_plugin.cpp
@@ -38,6 +38,7 @@
#include "scene/gui/button.h"
#include "scene/gui/flow_container.h"
#include "scene/gui/separator.h"
+#include "scene/resources/gradient_texture.h"
Point2 GradientTexture2DEdit::_get_handle_pos(const Handle p_handle) {
// Get the handle's mouse position in pixels relative to offset.
@@ -132,7 +133,7 @@ void GradientTexture2DEdit::gui_input(const Ref<InputEvent> &p_event) {
void GradientTexture2DEdit::set_texture(Ref<GradientTexture2D> &p_texture) {
texture = p_texture;
- texture->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
+ texture->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
}
void GradientTexture2DEdit::set_snap_enabled(bool p_snap_enabled) {
diff --git a/editor/plugins/gradient_texture_2d_editor_plugin.h b/editor/plugins/gradient_texture_2d_editor_plugin.h
index 2816b11d74..33570593cc 100644
--- a/editor/plugins/gradient_texture_2d_editor_plugin.h
+++ b/editor/plugins/gradient_texture_2d_editor_plugin.h
@@ -36,6 +36,7 @@
class Button;
class EditorSpinSlider;
+class GradientTexture2D;
class GradientTexture2DEdit : public Control {
GDCLASS(GradientTexture2DEdit, Control);
diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp
index be36447432..9a54a8c1a1 100644
--- a/editor/plugins/input_event_editor_plugin.cpp
+++ b/editor/plugins/input_event_editor_plugin.cpp
@@ -77,7 +77,7 @@ void InputEventConfigContainer::set_event(const Ref<InputEvent> &p_event) {
input_event = p_event;
_event_changed();
- input_event->connect("changed", callable_mp(this, &InputEventConfigContainer::_event_changed));
+ input_event->connect_changed(callable_mp(this, &InputEventConfigContainer::_event_changed));
}
InputEventConfigContainer::InputEventConfigContainer() {
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index d026916668..68d3661d10 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -4929,7 +4929,7 @@ void Node3DEditorViewport::register_shortcut_action(const String &p_path, const
Ref<Shortcut> sc = ED_SHORTCUT(p_path, p_name, p_keycode, p_physical);
shortcut_changed_callback(sc, p_path);
// Connect to the change event on the shortcut so the input binding can be updated.
- sc->connect("changed", callable_mp(this, &Node3DEditorViewport::shortcut_changed_callback).bind(sc, p_path));
+ sc->connect_changed(callable_mp(this, &Node3DEditorViewport::shortcut_changed_callback).bind(sc, p_path));
}
// Update the action in the InputMap to the provided shortcut events.
diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp
index efbb2b0d2b..ceff9cb5f3 100644
--- a/editor/plugins/polygon_3d_editor_plugin.cpp
+++ b/editor/plugins/polygon_3d_editor_plugin.cpp
@@ -30,7 +30,6 @@
#include "polygon_3d_editor_plugin.h"
-#include "core/core_string_names.h"
#include "core/input/input.h"
#include "core/io/file_access.h"
#include "core/math/geometry_2d.h"
@@ -497,7 +496,7 @@ void Polygon3DEditor::edit(Node *p_node) {
node_resource = node->call("_get_editable_3d_polygon_resource");
if (node_resource.is_valid()) {
- node_resource->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Polygon3DEditor::_polygon_draw));
+ node_resource->connect_changed(callable_mp(this, &Polygon3DEditor::_polygon_draw));
}
//Enable the pencil tool if the polygon is empty
if (_get_polygon().is_empty()) {
@@ -518,7 +517,7 @@ void Polygon3DEditor::edit(Node *p_node) {
} else {
node = nullptr;
if (node_resource.is_valid()) {
- node_resource->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Polygon3DEditor::_polygon_draw));
+ node_resource->disconnect_changed(callable_mp(this, &Polygon3DEditor::_polygon_draw));
}
node_resource.unref();
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 791937dbf6..78ba26503d 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -2435,6 +2435,18 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
return true;
}
+PackedStringArray ScriptEditor::get_unsaved_scripts() const {
+ PackedStringArray unsaved_list;
+
+ for (int i = 0; i < tab_container->get_tab_count(); i++) {
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
+ if (se && se->is_unsaved()) {
+ unsaved_list.append(se->get_name());
+ }
+ }
+ return unsaved_list;
+}
+
void ScriptEditor::save_current_script() {
ScriptEditorBase *current = _get_current_editor();
if (!current || _test_script_times_on_disk()) {
@@ -4207,8 +4219,53 @@ void ScriptEditorPlugin::selected_notify() {
_focus_another_editor();
}
+String ScriptEditorPlugin::get_unsaved_status(const String &p_for_scene) const {
+ const PackedStringArray unsaved_scripts = script_editor->get_unsaved_scripts();
+ if (unsaved_scripts.is_empty()) {
+ return String();
+ }
+
+ PackedStringArray message;
+ if (!p_for_scene.is_empty()) {
+ PackedStringArray unsaved_built_in_scripts;
+
+ const String scene_file = p_for_scene.get_file();
+ for (const String &E : unsaved_scripts) {
+ if (!E.is_resource_file() && E.contains(scene_file)) {
+ unsaved_built_in_scripts.append(E);
+ }
+ }
+
+ if (unsaved_built_in_scripts.is_empty()) {
+ return String();
+ } else {
+ message.resize(unsaved_built_in_scripts.size() + 1);
+ message.write[0] = TTR("There are unsaved changes in the following built-in script(s):");
+
+ int i = 1;
+ for (const String &E : unsaved_built_in_scripts) {
+ message.write[i] = E.trim_suffix("(*)");
+ i++;
+ }
+ return String("\n").join(message);
+ }
+ }
+
+ message.resize(unsaved_scripts.size() + 1);
+ message.write[0] = TTR("Save changes to the following script(s) before quitting?");
+
+ int i = 1;
+ for (const String &E : unsaved_scripts) {
+ message.write[i] = E.trim_suffix("(*)");
+ i++;
+ }
+ return String("\n").join(message);
+}
+
void ScriptEditorPlugin::save_external_data() {
- script_editor->save_all_scripts();
+ if (!EditorNode::get_singleton()->is_exiting()) {
+ script_editor->save_all_scripts();
+ }
}
void ScriptEditorPlugin::apply_changes() {
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index e879920e41..198aaa6c4e 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -512,6 +512,7 @@ public:
void get_breakpoints(List<String> *p_breakpoints);
+ PackedStringArray get_unsaved_scripts() const;
void save_current_script();
void save_all_scripts();
@@ -572,6 +573,7 @@ public:
virtual void make_visible(bool p_visible) override;
virtual void selected_notify() override;
+ virtual String get_unsaved_status(const String &p_for_scene) const override;
virtual void save_external_data() override;
virtual void apply_changes() override;
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 5fdfb21176..0a1f735f64 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1726,9 +1726,17 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
String variable_name = String(node->get_name()).to_snake_case().validate_identifier();
if (use_type) {
- text_to_drop += vformat("@onready var %s: %s = %s%s\n", variable_name, node->get_class_name(), is_unique ? "%" : "$", path);
+ StringName class_name = node->get_class_name();
+ Ref<Script> node_script = node->get_script();
+ if (node_script.is_valid()) {
+ StringName global_node_script_name = node_script->get_global_name();
+ if (global_node_script_name != StringName()) {
+ class_name = global_node_script_name;
+ }
+ }
+ text_to_drop += vformat("@onready var %s: %s = %c%s\n", variable_name, class_name, is_unique ? '%' : '$', path);
} else {
- text_to_drop += vformat("@onready var %s = %s%s\n", variable_name, is_unique ? "%" : "$", path);
+ text_to_drop += vformat("@onready var %s = %c%s\n", variable_name, is_unique ? '%' : '$', path);
}
}
} else {
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 268828e8f5..247586fbfc 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -287,6 +287,27 @@ void ShaderEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
p_layout->set_value("ShaderEditor", "selected_shader", selected_shader);
}
+String ShaderEditorPlugin::get_unsaved_status(const String &p_for_scene) const {
+ if (!p_for_scene.is_empty()) {
+ // TODO: handle built-in shaders.
+ return String();
+ }
+
+ // TODO: This should also include visual shaders and shader includes, but save_external_data() doesn't seem to save them...
+ PackedStringArray unsaved_shaders;
+ for (uint32_t i = 0; i < edited_shaders.size(); i++) {
+ if (edited_shaders[i].shader_editor) {
+ if (edited_shaders[i].shader_editor->is_unsaved()) {
+ if (unsaved_shaders.is_empty()) {
+ unsaved_shaders.append(TTR("Save changes to the following shaders(s) before quitting?"));
+ }
+ unsaved_shaders.append(edited_shaders[i].shader_editor->get_name());
+ }
+ }
+ }
+ return String("\n").join(unsaved_shaders);
+}
+
void ShaderEditorPlugin::save_external_data() {
for (EditedShader &edited_shader : edited_shaders) {
if (edited_shader.shader_editor) {
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index 45b48a2f91..fb7b283266 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -115,6 +115,7 @@ public:
virtual void set_window_layout(Ref<ConfigFile> p_layout) override;
virtual void get_window_layout(Ref<ConfigFile> p_layout) override;
+ virtual String get_unsaved_status(const String &p_for_scene) const override;
virtual void save_external_data() override;
virtual void apply_changes() override;
diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp
index f9aa14dd09..b0e532b136 100644
--- a/editor/plugins/shader_file_editor_plugin.cpp
+++ b/editor/plugins/shader_file_editor_plugin.cpp
@@ -222,7 +222,7 @@ void ShaderFileEditor::_bind_methods() {
void ShaderFileEditor::edit(const Ref<RDShaderFile> &p_shader) {
if (p_shader.is_null()) {
if (shader_file.is_valid()) {
- shader_file->disconnect("changed", callable_mp(this, &ShaderFileEditor::_shader_changed));
+ shader_file->disconnect_changed(callable_mp(this, &ShaderFileEditor::_shader_changed));
}
return;
}
@@ -234,7 +234,7 @@ void ShaderFileEditor::edit(const Ref<RDShaderFile> &p_shader) {
shader_file = p_shader;
if (shader_file.is_valid()) {
- shader_file->connect("changed", callable_mp(this, &ShaderFileEditor::_shader_changed));
+ shader_file->connect_changed(callable_mp(this, &ShaderFileEditor::_shader_changed));
}
_update_options();
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 563398e512..6b0e87e5e8 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -46,6 +46,7 @@
#include "scene/gui/option_button.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/separator.h"
+#include "scene/resources/atlas_texture.h"
static void _draw_shadowed_line(Control *p_control, const Point2 &p_from, const Size2 &p_size, const Size2 &p_shadow_offset, Color p_color, Color p_shadow_color) {
p_control->draw_line(p_from, p_from + p_size, p_color);
diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h
index a53f8f73d9..ed75be9061 100644
--- a/editor/plugins/sprite_frames_editor_plugin.h
+++ b/editor/plugins/sprite_frames_editor_plugin.h
@@ -44,6 +44,7 @@
#include "scene/gui/split_container.h"
#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
+#include "scene/resources/image_texture.h"
class OptionButton;
class EditorFileDialog;
diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp
index c126aec008..1d14f5e60b 100644
--- a/editor/plugins/style_box_editor_plugin.cpp
+++ b/editor/plugins/style_box_editor_plugin.cpp
@@ -32,6 +32,7 @@
#include "editor/editor_scale.h"
#include "scene/gui/button.h"
+#include "scene/resources/style_box_texture.h"
bool StyleBoxPreview::grid_preview_enabled = true;
@@ -42,11 +43,11 @@ void StyleBoxPreview::_grid_preview_toggled(bool p_active) {
void StyleBoxPreview::edit(const Ref<StyleBox> &p_stylebox) {
if (stylebox.is_valid()) {
- stylebox->disconnect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
+ stylebox->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
}
stylebox = p_stylebox;
if (stylebox.is_valid()) {
- stylebox->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
+ stylebox->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
}
Ref<StyleBoxTexture> sbt = stylebox;
grid_preview->set_visible(sbt.is_valid());
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index 4838661fa8..ed98c7f85c 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -122,7 +122,7 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const Stri
return;
}
if (shader.is_valid()) {
- shader->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
+ shader->disconnect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed));
}
shader = p_shader;
shader_inc = Ref<ShaderInclude>();
@@ -130,7 +130,7 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const Stri
set_edited_code(p_code);
if (shader.is_valid()) {
- shader->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
+ shader->connect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed));
}
}
@@ -152,7 +152,7 @@ void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_sha
return;
}
if (shader_inc.is_valid()) {
- shader_inc->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
+ shader_inc->disconnect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed));
}
shader_inc = p_shader_inc;
shader = Ref<Shader>();
@@ -160,7 +160,7 @@ void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_sha
set_edited_code(p_code);
if (shader_inc.is_valid()) {
- shader_inc->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed));
+ shader_inc->connect_changed(callable_mp(this, &ShaderTextEditor::_shader_changed));
}
}
diff --git a/editor/plugins/texture_3d_editor_plugin.cpp b/editor/plugins/texture_3d_editor_plugin.cpp
index 9904b888f2..2702e94188 100644
--- a/editor/plugins/texture_3d_editor_plugin.cpp
+++ b/editor/plugins/texture_3d_editor_plugin.cpp
@@ -115,7 +115,7 @@ void Texture3DEditor::_texture_rect_update_area() {
void Texture3DEditor::edit(Ref<Texture3D> p_texture) {
if (!texture.is_null()) {
- texture->disconnect("changed", callable_mp(this, &Texture3DEditor::_texture_changed));
+ texture->disconnect_changed(callable_mp(this, &Texture3DEditor::_texture_changed));
}
texture = p_texture;
@@ -125,7 +125,7 @@ void Texture3DEditor::edit(Ref<Texture3D> p_texture) {
_make_shaders();
}
- texture->connect("changed", callable_mp(this, &Texture3DEditor::_texture_changed));
+ texture->connect_changed(callable_mp(this, &Texture3DEditor::_texture_changed));
queue_redraw();
texture_rect->set_material(material);
setting = true;
@@ -176,7 +176,7 @@ Texture3DEditor::Texture3DEditor() {
Texture3DEditor::~Texture3DEditor() {
if (!texture.is_null()) {
- texture->disconnect("changed", callable_mp(this, &Texture3DEditor::_texture_changed));
+ texture->disconnect_changed(callable_mp(this, &Texture3DEditor::_texture_changed));
}
}
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index 1a9e09f3b1..87b207ebcd 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -29,9 +29,14 @@
/**************************************************************************/
#include "texture_editor_plugin.h"
+
#include "editor/editor_scale.h"
#include "scene/gui/label.h"
#include "scene/gui/texture_rect.h"
+#include "scene/resources/animated_texture.h"
+#include "scene/resources/atlas_texture.h"
+#include "scene/resources/compressed_texture.h"
+#include "scene/resources/image_texture.h"
TextureRect *TexturePreview::get_texture_display() {
return texture_display;
@@ -135,7 +140,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
metadata_label = memnew(Label);
_update_metadata_label_text();
- p_texture->connect("changed", callable_mp(this, &TexturePreview::_update_metadata_label_text));
+ p_texture->connect_changed(callable_mp(this, &TexturePreview::_update_metadata_label_text));
// It's okay that these colors are static since the grid color is static too.
metadata_label->add_theme_color_override("font_color", Color::named("white"));
diff --git a/editor/plugins/texture_layered_editor_plugin.cpp b/editor/plugins/texture_layered_editor_plugin.cpp
index 816d081617..a0188b08e5 100644
--- a/editor/plugins/texture_layered_editor_plugin.cpp
+++ b/editor/plugins/texture_layered_editor_plugin.cpp
@@ -181,7 +181,7 @@ void TextureLayeredEditor::_texture_rect_update_area() {
void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) {
if (!texture.is_null()) {
- texture->disconnect("changed", callable_mp(this, &TextureLayeredEditor::_texture_changed));
+ texture->disconnect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed));
}
texture = p_texture;
@@ -191,7 +191,7 @@ void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) {
_make_shaders();
}
- texture->connect("changed", callable_mp(this, &TextureLayeredEditor::_texture_changed));
+ texture->connect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed));
queue_redraw();
texture_rect->set_material(materials[texture->get_layered_type()]);
setting = true;
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index f2e650a604..19df31a0b3 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -30,7 +30,6 @@
#include "texture_region_editor_plugin.h"
-#include "core/core_string_names.h"
#include "core/input/input.h"
#include "core/os/keyboard.h"
#include "editor/editor_node.h"
@@ -42,7 +41,7 @@
#include "scene/gui/separator.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/view_panner.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/atlas_texture.h"
void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to) {
Vector2 line = (to - from).normalized() * 10;
@@ -433,7 +432,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
} else if (obj_styleBox.is_valid()) {
undo_redo->add_do_method(obj_styleBox.ptr(), "set_texture_margin", side[edited_margin], obj_styleBox->get_texture_margin(side[edited_margin]));
undo_redo->add_undo_method(obj_styleBox.ptr(), "set_texture_margin", side[edited_margin], prev_margin);
- obj_styleBox->emit_signal(CoreStringNames::get_singleton()->changed);
+ obj_styleBox->emit_changed();
}
edited_margin = -1;
} else {
@@ -913,10 +912,10 @@ void TextureRegionEditor::edit(Object *p_obj) {
node_ninepatch->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
}
if (obj_styleBox.is_valid()) {
- obj_styleBox->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
+ obj_styleBox->disconnect_changed(callable_mp(this, &TextureRegionEditor::_texture_changed));
}
if (atlas_tex.is_valid()) {
- atlas_tex->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
+ atlas_tex->disconnect_changed(callable_mp(this, &TextureRegionEditor::_texture_changed));
}
node_sprite_2d = nullptr;
@@ -941,7 +940,7 @@ void TextureRegionEditor::edit(Object *p_obj) {
}
if (is_resource) {
- p_obj->connect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
+ Object::cast_to<Resource>(p_obj)->connect_changed(callable_mp(this, &TextureRegionEditor::_texture_changed));
} else {
p_obj->connect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
}
diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h
index c303cec3f5..6b7a198246 100644
--- a/editor/plugins/texture_region_editor_plugin.h
+++ b/editor/plugins/texture_region_editor_plugin.h
@@ -38,11 +38,11 @@
#include "scene/3d/sprite_3d.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/nine_patch_rect.h"
-#include "scene/resources/style_box.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/style_box_texture.h"
-class ViewPanner;
+class AtlasTexture;
class OptionButton;
+class ViewPanner;
class TextureRegionEditor : public AcceptDialog {
GDCLASS(TextureRegionEditor, AcceptDialog);
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 09053db122..a1ddfc4b85 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -3151,7 +3151,7 @@ void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_ite
void ThemeTypeEditor::_change_pinned_stylebox() {
if (leading_stylebox.pinned) {
if (leading_stylebox.stylebox.is_valid()) {
- leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
}
Ref<StyleBox> new_stylebox = edited_theme->get_stylebox(leading_stylebox.item_name, edited_type);
@@ -3159,10 +3159,10 @@ void ThemeTypeEditor::_change_pinned_stylebox() {
leading_stylebox.ref_stylebox = (new_stylebox.is_valid() ? new_stylebox->duplicate() : Ref<Resource>());
if (leading_stylebox.stylebox.is_valid()) {
- new_stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ new_stylebox->connect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
}
} else if (leading_stylebox.stylebox.is_valid()) {
- leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
}
}
@@ -3187,7 +3187,7 @@ void ThemeTypeEditor::_on_pin_leader_button_pressed(Control *p_editor, String p_
void ThemeTypeEditor::_pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_stylebox) {
if (leading_stylebox.stylebox.is_valid()) {
- leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
}
LeadingStylebox leader;
@@ -3198,7 +3198,7 @@ void ThemeTypeEditor::_pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_
leading_stylebox = leader;
if (p_stylebox.is_valid()) {
- p_stylebox->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ p_stylebox->connect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
}
_update_type_items();
@@ -3214,7 +3214,7 @@ void ThemeTypeEditor::_on_unpin_leader_button_pressed() {
void ThemeTypeEditor::_unpin_leading_stylebox() {
if (leading_stylebox.stylebox.is_valid()) {
- leading_stylebox.stylebox->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
+ leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
}
LeadingStylebox leader;
@@ -3337,12 +3337,12 @@ void ThemeTypeEditor::_bind_methods() {
void ThemeTypeEditor::set_edited_theme(const Ref<Theme> &p_theme) {
if (edited_theme.is_valid()) {
- edited_theme->disconnect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
+ edited_theme->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
}
edited_theme = p_theme;
if (edited_theme.is_valid()) {
- edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
+ edited_theme->connect_changed(callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
_update_type_list();
}
diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp
index 7ed9c9d61b..937480eb50 100644
--- a/editor/plugins/tiles/atlas_merging_dialog.cpp
+++ b/editor/plugins/tiles/atlas_merging_dialog.cpp
@@ -36,6 +36,7 @@
#include "editor/gui/editor_file_dialog.h"
#include "scene/gui/control.h"
#include "scene/gui/split_container.h"
+#include "scene/resources/image_texture.h"
void AtlasMergingDialog::_property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) {
_set(p_property, p_value);
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 7767831ea3..24e61d7dc9 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -88,11 +88,11 @@ void TileDataEditor::_bind_methods() {
void TileDataEditor::set_tile_set(Ref<TileSet> p_tile_set) {
if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update));
+ tile_set->disconnect_changed(callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update));
}
tile_set = p_tile_set;
if (tile_set.is_valid()) {
- tile_set->connect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update));
+ tile_set->connect_changed(callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update));
}
_tile_set_changed_plan_update();
}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 4e8c28e997..3a2d71ddf9 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2104,7 +2104,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() {
void TileSetAtlasSourceEditor::_tile_set_changed() {
if (tile_set->get_source_count() == 0) {
// No sources, so nothing to do here anymore.
- tile_set->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
+ tile_set->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
tile_set = Ref<TileSet>();
return;
}
@@ -2218,7 +2218,7 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
// Remove listener for old objects.
if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
+ tile_set->disconnect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
}
// Clear the selection.
@@ -2233,7 +2233,7 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
read_only = new_read_only_state;
if (tile_set.is_valid()) {
- tile_set->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
+ tile_set->connect_changed(callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
}
if (read_only && tools_button_group->get_pressed_button() == tool_paint_button) {
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index 9821588f63..9725ee72bb 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -723,7 +723,7 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
// Remove listener.
if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
+ tile_set->disconnect_changed(callable_mp(this, &TileSetEditor::_tile_set_changed));
}
// Change the edited object.
@@ -738,7 +738,7 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
sources_advanced_menu_button->set_disabled(read_only);
source_sort_button->set_disabled(read_only);
- tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
+ tile_set->connect_changed(callable_mp(this, &TileSetEditor::_tile_set_changed));
if (first_edit) {
first_edit = false;
_set_source_sort(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "tile_source_sort", 0));
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 6908dd7c3b..5ffa7b4bd4 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -384,7 +384,7 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS
// Remove listener for old objects.
if (tile_set_scenes_collection_source) {
- tile_set_scenes_collection_source->disconnect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed));
+ tile_set_scenes_collection_source->disconnect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed));
}
// Change the edited object.
@@ -404,7 +404,7 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS
// Add the listener again.
if (tile_set_scenes_collection_source) {
- tile_set_scenes_collection_source->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed));
+ tile_set_scenes_collection_source->connect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed));
}
// Update everything.
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index b2ee3103ce..74668bd3be 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -45,6 +45,7 @@
#include "scene/gui/button.h"
#include "scene/gui/control.h"
#include "scene/gui/separator.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/tile_set.h"
TilesEditorPlugin *TilesEditorPlugin::singleton = nullptr;
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index c9651e634f..f180f72cdc 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -31,7 +31,6 @@
#include "visual_shader_editor_plugin.h"
#include "core/config/project_settings.h"
-#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
#include "core/os/keyboard.h"
@@ -57,6 +56,9 @@
#include "scene/gui/tree.h"
#include "scene/gui/view_panner.h"
#include "scene/main/window.h"
+#include "scene/resources/curve_texture.h"
+#include "scene/resources/image_texture.h"
+#include "scene/resources/style_box_flat.h"
#include "scene/resources/visual_shader_nodes.h"
#include "scene/resources/visual_shader_particle_nodes.h"
#include "servers/display_server.h"
@@ -239,7 +241,7 @@ void VisualShaderGraphPlugin::update_curve(int p_node_id) {
if (tex->get_texture().is_valid()) {
links[p_node_id].curve_editors[0]->set_curve(tex->get_texture()->get_curve());
}
- tex->emit_signal(CoreStringNames::get_singleton()->changed);
+ tex->emit_changed();
}
}
@@ -253,7 +255,7 @@ void VisualShaderGraphPlugin::update_curve_xyz(int p_node_id) {
links[p_node_id].curve_editors[1]->set_curve(tex->get_texture()->get_curve_y());
links[p_node_id].curve_editors[2]->set_curve(tex->get_texture()->get_curve_z());
}
- tex->emit_signal(CoreStringNames::get_singleton()->changed);
+ tex->emit_changed();
}
}
@@ -560,9 +562,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
if (curve.is_valid()) {
custom_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- Callable ce = callable_mp(graph_plugin, &VisualShaderGraphPlugin::update_curve);
- if (curve->get_texture().is_valid() && !curve->get_texture()->is_connected("changed", ce)) {
- curve->get_texture()->connect("changed", ce.bind(p_id));
+ if (curve->get_texture().is_valid()) {
+ curve->get_texture()->connect_changed(callable_mp(graph_plugin, &VisualShaderGraphPlugin::update_curve).bind(p_id));
}
CurveEditor *curve_editor = memnew(CurveEditor);
@@ -578,9 +579,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
if (curve_xyz.is_valid()) {
custom_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- Callable ce = callable_mp(graph_plugin, &VisualShaderGraphPlugin::update_curve_xyz);
- if (curve_xyz->get_texture().is_valid() && !curve_xyz->get_texture()->is_connected("changed", ce)) {
- curve_xyz->get_texture()->connect("changed", ce.bind(p_id));
+ if (curve_xyz->get_texture().is_valid()) {
+ curve_xyz->get_texture()->connect_changed(callable_mp(graph_plugin, &VisualShaderGraphPlugin::update_curve_xyz).bind(p_id));
}
CurveEditor *curve_editor_x = memnew(CurveEditor);
@@ -1159,20 +1159,14 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) {
visual_shader = Ref<VisualShader>(p_visual_shader);
graph_plugin->register_shader(visual_shader.ptr());
- Callable ce = callable_mp(this, &VisualShaderEditor::_update_preview);
- if (!visual_shader->is_connected("changed", ce)) {
- visual_shader->connect("changed", ce);
- }
+ visual_shader->connect_changed(callable_mp(this, &VisualShaderEditor::_update_preview));
visual_shader->set_graph_offset(graph->get_scroll_ofs() / EDSCALE);
_set_mode(visual_shader->get_mode());
_update_nodes();
} else {
if (visual_shader.is_valid()) {
- Callable ce = callable_mp(this, &VisualShaderEditor::_update_preview);
- if (visual_shader->is_connected("changed", ce)) {
- visual_shader->disconnect("changed", ce);
- }
+ visual_shader->disconnect_changed(callable_mp(this, &VisualShaderEditor::_update_preview));
}
visual_shader.unref();
}
@@ -3089,6 +3083,9 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
if (!is_native) {
vsnode->set_script(add_options[p_idx].script);
}
+ VisualShaderNodeCustom *custom_node = Object::cast_to<VisualShaderNodeCustom>(vsn);
+ ERR_FAIL_COND(!custom_node);
+ custom_node->update_ports();
}
bool is_texture2d = (Object::cast_to<VisualShaderNodeTexture>(vsnode.ptr()) != nullptr);
@@ -3217,16 +3214,26 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
} else {
- // Attempting to connect to the first correct port.
+ int _to_slot = -1;
+
+ // Attempting to connect to the default input port or to the first correct port (if it's not found).
for (int i = 0; i < vsnode->get_input_port_count(); i++) {
if (visual_shader->is_port_types_compatible(output_port_type, vsnode->get_input_port_type(i))) {
- undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, i);
- undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, i);
- undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, i);
- undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, i);
- break;
+ if (i == vsnode->get_default_input_port(output_port_type)) {
+ _to_slot = i;
+ break;
+ } else if (_to_slot == -1) {
+ _to_slot = i;
+ }
}
}
+
+ if (_to_slot >= 0) {
+ undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_slot, _to_node, _to_slot);
+ undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_slot, _to_node, _to_slot);
+ }
}
if (output_port_type == VisualShaderNode::PORT_TYPE_SAMPLER) {
@@ -6447,7 +6454,7 @@ public:
properties[i]->update_property();
properties[i]->set_name_split_ratio(0);
}
- node->connect("changed", callable_mp(this, &VisualShaderNodePluginDefaultEditor::_node_changed));
+ node->connect_changed(callable_mp(this, &VisualShaderNodePluginDefaultEditor::_node_changed));
}
static void _bind_methods() {
@@ -6713,7 +6720,7 @@ void VisualShaderNodePortPreview::_shader_changed() {
void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, VisualShader::Type p_type, int p_node, int p_port, bool p_is_valid) {
shader = p_shader;
- shader->connect("changed", callable_mp(this, &VisualShaderNodePortPreview::_shader_changed), CONNECT_DEFERRED);
+ shader->connect_changed(callable_mp(this, &VisualShaderNodePortPreview::_shader_changed), CONNECT_DEFERRED);
type = p_type;
port = p_port;
node = p_node;
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 6df6973a7d..691adcdb7a 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -59,6 +59,7 @@
#include "scene/gui/separator.h"
#include "scene/gui/texture_rect.h"
#include "scene/main/window.h"
+#include "scene/resources/image_texture.h"
#include "servers/display_server.h"
#include "servers/navigation_server_3d.h"
#include "servers/physics_server_2d.h"
diff --git a/main/main.cpp b/main/main.cpp
index 989b9bc59d..7e2741648d 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2710,6 +2710,9 @@ bool Main::start() {
}
uint64_t minimum_time_msec = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/boot_splash/minimum_display_time", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:ms"), 0);
+ if (Engine::get_singleton()->is_editor_hint()) {
+ minimum_time_msec = 0;
+ }
#ifdef TOOLS_ENABLED
#ifdef MODULE_GDSCRIPT_ENABLED
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index c241f1cabd..4c217dac28 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -953,12 +953,12 @@ void CSGMesh3D::set_mesh(const Ref<Mesh> &p_mesh) {
return;
}
if (mesh.is_valid()) {
- mesh->disconnect("changed", callable_mp(this, &CSGMesh3D::_mesh_changed));
+ mesh->disconnect_changed(callable_mp(this, &CSGMesh3D::_mesh_changed));
}
mesh = p_mesh;
if (mesh.is_valid()) {
- mesh->connect("changed", callable_mp(this, &CSGMesh3D::_mesh_changed));
+ mesh->connect_changed(callable_mp(this, &CSGMesh3D::_mesh_changed));
}
_mesh_changed();
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index e6523e3d09..8a3a36e84b 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -31,6 +31,7 @@
#include "texture_loader_dds.h"
#include "core/io/file_access.h"
+#include "scene/resources/image_texture.h"
#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])))
diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h
index dc3df1fcee..3763700ff1 100644
--- a/modules/dds/texture_loader_dds.h
+++ b/modules/dds/texture_loader_dds.h
@@ -32,7 +32,6 @@
#define TEXTURE_LOADER_DDS_H
#include "core/io/resource_loader.h"
-#include "scene/resources/texture.h"
class ResourceFormatDDS : public ResourceFormatLoader {
public:
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 3d6d133579..7117337827 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -787,11 +787,11 @@ Error GDScript::reload(bool p_keep_state) {
err = compiler.compile(&parser, this, p_keep_state);
if (err) {
+ _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
if (can_run) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
}
- _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
reloading = false;
return ERR_COMPILATION_FAILED;
} else {
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index e5bb93e3c8..d191bd0224 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -294,8 +294,12 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
if (p_update_from_disk) {
r_error = script->load_source_code(p_path);
+ if (r_error) {
+ return script;
+ }
}
+ r_error = script->reload(true);
if (r_error) {
return script;
}
@@ -303,7 +307,6 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
singleton->full_gdscript_cache[p_path] = script;
singleton->shallow_gdscript_cache.erase(p_path);
- script->reload(true);
return script;
}
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 004af80a91..3f571602e8 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -2579,9 +2579,9 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
}
} else if (!base->is_valid()) {
Error err = OK;
- Ref<GDScript> base_root = GDScriptCache::get_full_script(base->path, err, p_script->path);
+ Ref<GDScript> base_root = GDScriptCache::get_shallow_script(base->path, err, p_script->path);
if (err) {
- _set_error(vformat(R"(Could not compile base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr);
+ _set_error(vformat(R"(Could not parse base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr);
return err;
}
if (base_root.is_valid()) {
@@ -2591,7 +2591,12 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
_set_error(vformat(R"(Could not find class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);
return ERR_COMPILATION_FAILED;
}
- ERR_FAIL_COND_V(!base->is_valid() && !base->reloading, ERR_BUG);
+
+ err = _populate_class_members(base.ptr(), p_class->base_type.class_type, p_keep_state);
+ if (err) {
+ _set_error(vformat(R"(Could not populate class members of base class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);
+ return err;
+ }
}
p_script->base = base;
@@ -2968,7 +2973,7 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
GDScriptCache::add_static_script(p_script);
}
- return GDScriptCache::finish_compiling(main_script->get_path());
+ return GDScriptCache::finish_compiling(main_script->path);
}
String GDScriptCompiler::get_error() const {
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index e23bd50b8b..605e82be6e 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -98,7 +98,7 @@ public:
return;
}
- virtual String _get_name() const override { return "GDScript"; }
+ virtual String get_name() const override { return "GDScript"; }
};
static void _editor_init() {
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 0bbc2bbf74..d828363e03 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -49,6 +49,8 @@
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/multimesh_instance_3d.h"
+#include "scene/resources/image_texture.h"
+#include "scene/resources/portable_compressed_texture.h"
#include "scene/resources/skin.h"
#include "scene/resources/surface_tool.h"
@@ -3769,6 +3771,12 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) {
extensions["KHR_materials_unlit"] = mat_unlit;
p_state->add_used_extension("KHR_materials_unlit");
}
+ if (base_material->get_feature(BaseMaterial3D::FEATURE_EMISSION) && !Math::is_equal_approx(base_material->get_emission_energy_multiplier(), 1.0f)) {
+ Dictionary mat_emissive_strength;
+ mat_emissive_strength["emissiveStrength"] = base_material->get_emission_energy_multiplier();
+ extensions["KHR_materials_emissive_strength"] = mat_emissive_strength;
+ p_state->add_used_extension("KHR_materials_emissive_strength");
+ }
d["extensions"] = extensions;
materials.push_back(d);
@@ -3789,35 +3797,35 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) {
const Array &materials = p_state->json["materials"];
for (GLTFMaterialIndex i = 0; i < materials.size(); i++) {
- const Dictionary &d = materials[i];
+ const Dictionary &material_dict = materials[i];
Ref<StandardMaterial3D> material;
material.instantiate();
- if (d.has("name") && !String(d["name"]).is_empty()) {
- material->set_name(d["name"]);
+ if (material_dict.has("name") && !String(material_dict["name"]).is_empty()) {
+ material->set_name(material_dict["name"]);
} else {
material->set_name(vformat("material_%s", itos(i)));
}
material->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
- Dictionary pbr_spec_gloss_extensions;
- if (d.has("extensions")) {
- pbr_spec_gloss_extensions = d["extensions"];
+ Dictionary material_extensions;
+ if (material_dict.has("extensions")) {
+ material_extensions = material_dict["extensions"];
}
- if (pbr_spec_gloss_extensions.has("KHR_materials_unlit")) {
+ if (material_extensions.has("KHR_materials_unlit")) {
material->set_shading_mode(BaseMaterial3D::SHADING_MODE_UNSHADED);
}
- if (pbr_spec_gloss_extensions.has("KHR_materials_emissive_strength")) {
- Dictionary emissive_strength = pbr_spec_gloss_extensions["KHR_materials_emissive_strength"];
+ if (material_extensions.has("KHR_materials_emissive_strength")) {
+ Dictionary emissive_strength = material_extensions["KHR_materials_emissive_strength"];
if (emissive_strength.has("emissiveStrength")) {
material->set_emission_energy_multiplier(emissive_strength["emissiveStrength"]);
}
}
- if (pbr_spec_gloss_extensions.has("KHR_materials_pbrSpecularGlossiness")) {
+ if (material_extensions.has("KHR_materials_pbrSpecularGlossiness")) {
WARN_PRINT("Material uses a specular and glossiness workflow. Textures will be converted to roughness and metallic workflow, which may not be 100% accurate.");
- Dictionary sgm = pbr_spec_gloss_extensions["KHR_materials_pbrSpecularGlossiness"];
+ Dictionary sgm = material_extensions["KHR_materials_pbrSpecularGlossiness"];
Ref<GLTFSpecGloss> spec_gloss;
spec_gloss.instantiate();
@@ -3865,8 +3873,8 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) {
}
spec_gloss_to_rough_metal(spec_gloss, material);
- } else if (d.has("pbrMetallicRoughness")) {
- const Dictionary &mr = d["pbrMetallicRoughness"];
+ } else if (material_dict.has("pbrMetallicRoughness")) {
+ const Dictionary &mr = material_dict["pbrMetallicRoughness"];
if (mr.has("baseColorFactor")) {
const Array &arr = mr["baseColorFactor"];
ERR_FAIL_COND_V(arr.size() != 4, ERR_PARSE_ERROR);
@@ -3918,8 +3926,8 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) {
}
}
- if (d.has("normalTexture")) {
- const Dictionary &bct = d["normalTexture"];
+ if (material_dict.has("normalTexture")) {
+ const Dictionary &bct = material_dict["normalTexture"];
if (bct.has("index")) {
material->set_texture(BaseMaterial3D::TEXTURE_NORMAL, _get_texture(p_state, bct["index"], TEXTURE_TYPE_NORMAL));
material->set_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING, true);
@@ -3928,8 +3936,8 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) {
material->set_normal_scale(bct["scale"]);
}
}
- if (d.has("occlusionTexture")) {
- const Dictionary &bct = d["occlusionTexture"];
+ if (material_dict.has("occlusionTexture")) {
+ const Dictionary &bct = material_dict["occlusionTexture"];
if (bct.has("index")) {
material->set_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC));
material->set_ao_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_RED);
@@ -3937,8 +3945,8 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) {
}
}
- if (d.has("emissiveFactor")) {
- const Array &arr = d["emissiveFactor"];
+ if (material_dict.has("emissiveFactor")) {
+ const Array &arr = material_dict["emissiveFactor"];
ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR);
const Color c = Color(arr[0], arr[1], arr[2]).linear_to_srgb();
material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true);
@@ -3946,8 +3954,8 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) {
material->set_emission(c);
}
- if (d.has("emissiveTexture")) {
- const Dictionary &bct = d["emissiveTexture"];
+ if (material_dict.has("emissiveTexture")) {
+ const Dictionary &bct = material_dict["emissiveTexture"];
if (bct.has("index")) {
material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC));
material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true);
@@ -3955,20 +3963,20 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) {
}
}
- if (d.has("doubleSided")) {
- const bool ds = d["doubleSided"];
+ if (material_dict.has("doubleSided")) {
+ const bool ds = material_dict["doubleSided"];
if (ds) {
material->set_cull_mode(BaseMaterial3D::CULL_DISABLED);
}
}
- if (d.has("alphaMode")) {
- const String &am = d["alphaMode"];
+ if (material_dict.has("alphaMode")) {
+ const String &am = material_dict["alphaMode"];
if (am == "BLEND") {
material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS);
} else if (am == "MASK") {
material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR);
- if (d.has("alphaCutoff")) {
- material->set_alpha_scissor_threshold(d["alphaCutoff"]);
+ if (material_dict.has("alphaCutoff")) {
+ material->set_alpha_scissor_threshold(material_dict["alphaCutoff"]);
} else {
material->set_alpha_scissor_threshold(0.5f);
}
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 6973bd3cd8..f9c3ca476a 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -138,11 +138,11 @@
Returns the position of a grid cell in the GridMap's local coordinate space. To convert the returned value into global coordinates, use [method Node3D.to_global]. See also [method map_to_local].
</description>
</method>
- <method name="resource_changed">
+ <method name="resource_changed" is_deprecated="true">
<return type="void" />
<param index="0" name="resource" type="Resource" />
<description>
- Notifies the [GridMap] about changed resource and recreates octant data.
+ [i]Obsoleted.[/i] Use [signal Resource.changed] instead.
</description>
</method>
<method name="set_cell_item">
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index c77fa98be2..f1e2218434 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -258,11 +258,11 @@ RID GridMap::get_navigation_map() const {
void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) {
if (!mesh_library.is_null()) {
- mesh_library->unregister_owner(this);
+ mesh_library->disconnect_changed(callable_mp(this, &GridMap::_recreate_octant_data));
}
mesh_library = p_mesh_library;
if (!mesh_library.is_null()) {
- mesh_library->register_owner(this);
+ mesh_library->connect_changed(callable_mp(this, &GridMap::_recreate_octant_data));
}
_recreate_octant_data();
@@ -1005,9 +1005,10 @@ void GridMap::clear() {
clear_baked_meshes();
}
+#ifndef DISABLE_DEPRECATED
void GridMap::resource_changed(const Ref<Resource> &p_res) {
- _recreate_octant_data();
}
+#endif
void GridMap::_update_octants_callback() {
if (!awaiting_update) {
@@ -1079,7 +1080,9 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_to_local", "map_position"), &GridMap::map_to_local);
ClassDB::bind_method(D_METHOD("_update_octants_callback"), &GridMap::_update_octants_callback);
+#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &GridMap::resource_changed);
+#endif
ClassDB::bind_method(D_METHOD("set_center_x", "enable"), &GridMap::set_center_x);
ClassDB::bind_method(D_METHOD("get_center_x"), &GridMap::get_center_x);
@@ -1336,10 +1339,6 @@ void GridMap::_navigation_map_changed(RID p_map) {
#endif // DEBUG_ENABLED
GridMap::~GridMap() {
- if (!mesh_library.is_null()) {
- mesh_library->unregister_owner(this);
- }
-
clear();
#ifdef DEBUG_ENABLED
NavigationServer3D::get_singleton()->disconnect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed));
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index 18c3f90269..e05979efbc 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -203,7 +203,9 @@ class GridMap : public Node3D {
void _queue_octants_dirty();
void _update_octants_callback();
+#ifndef DISABLE_DEPRECATED
void resource_changed(const Ref<Resource> &p_res);
+#endif
void _clear_internal();
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
index 8be1151142..72614dd7e0 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
@@ -384,5 +384,65 @@ namespace Godot.SourceGenerators
typeArgumentSyntax.GetLocation(),
typeArgumentSyntax.SyntaxTree.FilePath));
}
+
+ public static readonly DiagnosticDescriptor GlobalClassMustDeriveFromGodotObjectRule =
+ new DiagnosticDescriptor(id: "GD0401",
+ title: "The class must derive from GodotObject or a derived class",
+ messageFormat: "The class '{0}' must derive from GodotObject or a derived class.",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The class must derive from GodotObject or a derived class. Change the base class or remove the '[GlobalClass]' attribute.");
+
+ public static void ReportGlobalClassMustDeriveFromGodotObject(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode classSyntax,
+ ISymbol typeSymbol)
+ {
+ string message = $"The class '{typeSymbol.ToDisplayString()}' must derive from GodotObject or a derived class";
+
+ string description = $"{message}. Change the base class or remove the '[GlobalClass]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0401",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ classSyntax.GetLocation(),
+ classSyntax.SyntaxTree.FilePath));
+ }
+
+ public static readonly DiagnosticDescriptor GlobalClassMustNotBeGenericRule =
+ new DiagnosticDescriptor(id: "GD0402",
+ title: "The class must not contain generic arguments",
+ messageFormat: "The class '{0}' must not contain generic arguments",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The class must be a non-generic type. Remove the generic arguments or the '[GlobalClass]' attribute.");
+
+ public static void ReportGlobalClassMustNotBeGeneric(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode classSyntax,
+ ISymbol typeSymbol)
+ {
+ string message = $"The class '{typeSymbol.ToDisplayString()}' must not contain generic arguments";
+
+ string description = $"{message}. Remove the generic arguments or the '[GlobalClass]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0402",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ classSyntax.GetLocation(),
+ classSyntax.SyntaxTree.FilePath));
+ }
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
index b2a3c046e5..b6ea4b8e88 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -81,7 +81,7 @@ namespace Godot.SourceGenerators
return godotClassName ?? nativeType.Name;
}
- private static bool IsGodotScriptClass(
+ private static bool TryGetGodotScriptClass(
this ClassDeclarationSyntax cds, Compilation compilation,
out INamedTypeSymbol? symbol
)
@@ -108,7 +108,7 @@ namespace Godot.SourceGenerators
{
foreach (var cds in source)
{
- if (cds.IsGodotScriptClass(compilation, out var symbol))
+ if (cds.TryGetGodotScriptClass(compilation, out var symbol))
yield return (cds, symbol!);
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs
new file mode 100644
index 0000000000..bcb35dae8a
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs
@@ -0,0 +1,42 @@
+using System.Collections.Immutable;
+using System.Linq;
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Godot.SourceGenerators
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class GlobalClassAnalyzer : DiagnosticAnalyzer
+ {
+ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
+ => ImmutableArray.Create(
+ Common.GlobalClassMustDeriveFromGodotObjectRule,
+ Common.GlobalClassMustNotBeGenericRule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration);
+ }
+
+ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ {
+ var typeClassDecl = (ClassDeclarationSyntax)context.Node;
+
+ // Return if not a type symbol or the type is not a global class.
+ if (context.ContainingSymbol is not INamedTypeSymbol typeSymbol ||
+ !typeSymbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotGlobalClassAttribute() ?? false))
+ return;
+
+ if (typeSymbol.IsGenericType)
+ Common.ReportGlobalClassMustNotBeGeneric(context, typeClassDecl, typeSymbol);
+
+ if (!typeSymbol.InheritsFrom("GodotSharp", GodotClasses.GodotObject))
+ Common.ReportGlobalClassMustDeriveFromGodotObject(context, typeClassDecl, typeSymbol);
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
index 3e6858485d..09a4ab538f 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -620,7 +620,7 @@ namespace Godot.SourceGenerators
bool isPresetHint = false;
- if (elementVariantType == VariantType.String)
+ if (elementVariantType == VariantType.String || elementVariantType == VariantType.StringName)
isPresetHint = GetStringArrayEnumHint(elementVariantType, exportAttr, out hintString);
if (!isPresetHint)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 36f5d8e2ab..74425c9835 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
+using System.ComponentModel;
namespace Godot
{
@@ -623,21 +624,31 @@ namespace Godot
/// </summary>
/// <param name="target">The position to look at.</param>
/// <param name="up">The relative up direction.</param>
+ /// <param name="useModelFront">
+ /// If true, then the model is oriented in reverse,
+ /// towards the model front axis (+Z, Vector3.ModelFront),
+ /// which is more useful for orienting 3D models.
+ /// </param>
/// <returns>The resulting basis matrix.</returns>
- public static Basis LookingAt(Vector3 target, Vector3 up)
+ public static Basis LookingAt(Vector3 target, Vector3? up = null, bool useModelFront = false)
{
+ up ??= Vector3.Up;
#if DEBUG
if (target.IsZeroApprox())
{
throw new ArgumentException("The vector can't be zero.", nameof(target));
}
- if (up.IsZeroApprox())
+ if (up.Value.IsZeroApprox())
{
throw new ArgumentException("The vector can't be zero.", nameof(up));
}
#endif
- Vector3 column2 = -target.Normalized();
- Vector3 column0 = up.Cross(column2);
+ Vector3 column2 = target.Normalized();
+ if (!useModelFront)
+ {
+ column2 = -column2;
+ }
+ Vector3 column0 = up.Value.Cross(column2);
#if DEBUG
if (column0.IsZeroApprox())
{
@@ -649,6 +660,13 @@ namespace Godot
return new Basis(column0, column1, column2);
}
+ /// <inheritdoc cref="LookingAt(Vector3, Nullable{Vector3}, bool)"/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static Basis LookingAt(Vector3 target, Vector3 up)
+ {
+ return LookingAt(target, up, false);
+ }
+
/// <summary>
/// Returns the orthonormalized version of the basis matrix (useful to
/// call occasionally to avoid rounding errors for orthogonal matrices).
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 1e2aaa299f..ae2c025137 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
+using System.ComponentModel;
namespace Godot
{
@@ -175,14 +176,26 @@ namespace Godot
/// </summary>
/// <param name="target">The object to look at.</param>
/// <param name="up">The relative up direction.</param>
+ /// <param name="useModelFront">
+ /// If true, then the model is oriented in reverse,
+ /// towards the model front axis (+Z, Vector3.ModelFront),
+ /// which is more useful for orienting 3D models.
+ /// </param>
/// <returns>The resulting transform.</returns>
- public readonly Transform3D LookingAt(Vector3 target, Vector3 up)
+ public readonly Transform3D LookingAt(Vector3 target, Vector3? up = null, bool useModelFront = false)
{
Transform3D t = this;
- t.SetLookAt(Origin, target, up);
+ t.SetLookAt(Origin, target, up ?? Vector3.Up, useModelFront);
return t;
}
+ /// <inheritdoc cref="LookingAt(Vector3, Nullable{Vector3}, bool)"/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly Transform3D LookingAt(Vector3 target, Vector3 up)
+ {
+ return LookingAt(target, up, false);
+ }
+
/// <summary>
/// Returns the transform with the basis orthogonal (90 degrees),
/// and normalized axis vectors (scale of 1 or -1).
@@ -247,9 +260,9 @@ namespace Godot
return new Transform3D(Basis * tmpBasis, Origin);
}
- private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
+ private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up, bool useModelFront = false)
{
- Basis = Basis.LookingAt(target - eye, up);
+ Basis = Basis.LookingAt(target - eye, up, useModelFront);
Origin = eye;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index c773c0fda6..d929b5c6ab 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -660,6 +660,13 @@ namespace Godot
private static readonly Vector3 _forward = new Vector3(0, 0, -1);
private static readonly Vector3 _back = new Vector3(0, 0, 1);
+ private static readonly Vector3 _modelLeft = new Vector3(1, 0, 0);
+ private static readonly Vector3 _modelRight = new Vector3(-1, 0, 0);
+ private static readonly Vector3 _modelTop = new Vector3(0, 1, 0);
+ private static readonly Vector3 _modelBottom = new Vector3(0, -1, 0);
+ private static readonly Vector3 _modelFront = new Vector3(0, 0, 1);
+ private static readonly Vector3 _modelRear = new Vector3(0, 0, -1);
+
/// <summary>
/// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
@@ -712,6 +719,31 @@ namespace Godot
public static Vector3 Back { get { return _back; } }
/// <summary>
+ /// Unit vector pointing towards the left side of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelLeft { get { return _modelLeft; } }
+ /// <summary>
+ /// Unit vector pointing towards the right side of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelRight { get { return _modelRight; } }
+ /// <summary>
+ /// Unit vector pointing towards the top side (up) of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelTop { get { return _modelTop; } }
+ /// <summary>
+ /// Unit vector pointing towards the bottom side (down) of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelBottom { get { return _modelBottom; } }
+ /// <summary>
+ /// Unit vector pointing towards the front side (facing forward) of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelFront { get { return _modelFront; } }
+ /// <summary>
+ /// Unit vector pointing towards the rear side (back) of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelRear { get { return _modelRear; } }
+
+ /// <summary>
/// Constructs a new <see cref="Vector3"/> with the given components.
/// </summary>
/// <param name="x">The vector's X component.</param>
diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp
index e5207fdae2..9c2d281f72 100644
--- a/modules/multiplayer/multiplayer_synchronizer.cpp
+++ b/modules/multiplayer/multiplayer_synchronizer.cpp
@@ -157,7 +157,7 @@ Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Obj
bool valid = false;
const Object *obj = _get_prop_target(p_obj, prop);
ERR_FAIL_COND_V(!obj, FAILED);
- r_variant.write[i] = obj->get(prop.get_concatenated_subnames(), &valid);
+ r_variant.write[i] = obj->get_indexed(prop.get_subnames(), &valid);
r_variant_ptrs.write[i] = &r_variant[i];
ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop));
i++;
@@ -171,7 +171,7 @@ Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Obj
for (const NodePath &prop : p_properties) {
Object *obj = _get_prop_target(p_obj, prop);
ERR_FAIL_COND_V(!obj, FAILED);
- obj->set(prop.get_concatenated_subnames(), p_state[i]);
+ obj->set_indexed(prop.get_subnames(), p_state[i]);
i += 1;
}
return OK;
diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp
index 867cf5d8fc..9c0ce3e71e 100644
--- a/modules/navigation/nav_region.cpp
+++ b/modules/navigation/nav_region.cpp
@@ -115,11 +115,11 @@ void NavRegion::update_polygons() {
#ifdef DEBUG_ENABLED
if (!Math::is_equal_approx(double(map->get_cell_size()), double(mesh->get_cell_size()))) {
- ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a different `cell_size` than the `cell_size` set on the navigation map.");
+ ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(map->get_cell_size()), double(mesh->get_cell_size())));
}
if (!Math::is_equal_approx(double(map->get_cell_height()), double(mesh->get_cell_height()))) {
- ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a different `cell_height` than the `cell_height` set on the navigation map.");
+ ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_height` of %s while assigned to a navigation map set to a `cell_height` of %s. The cell height for navigation maps can be changed by using the NavigationServer map_set_cell_height() function. The cell height for default navigation maps can also be changed in the ProjectSettings.", double(map->get_cell_height()), double(mesh->get_cell_height())));
}
if (map && Math::rad_to_deg(map->get_up().angle_to(transform.basis.get_column(1))) >= 90.0f) {
diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp
index a7176e0816..1b0c5cb9e3 100644
--- a/modules/noise/noise_texture_2d.cpp
+++ b/modules/noise/noise_texture_2d.cpp
@@ -32,8 +32,6 @@
#include "noise.h"
-#include "core/core_string_names.h"
-
NoiseTexture2D::NoiseTexture2D() {
noise = Ref<Noise>();
@@ -223,11 +221,11 @@ void NoiseTexture2D::set_noise(Ref<Noise> p_noise) {
return;
}
if (noise.is_valid()) {
- noise->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update));
+ noise->disconnect_changed(callable_mp(this, &NoiseTexture2D::_queue_update));
}
noise = p_noise;
if (noise.is_valid()) {
- noise->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update));
+ noise->connect_changed(callable_mp(this, &NoiseTexture2D::_queue_update));
}
_queue_update();
}
@@ -347,11 +345,11 @@ void NoiseTexture2D::set_color_ramp(const Ref<Gradient> &p_gradient) {
return;
}
if (color_ramp.is_valid()) {
- color_ramp->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update));
+ color_ramp->disconnect_changed(callable_mp(this, &NoiseTexture2D::_queue_update));
}
color_ramp = p_gradient;
if (color_ramp.is_valid()) {
- color_ramp->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update));
+ color_ramp->connect_changed(callable_mp(this, &NoiseTexture2D::_queue_update));
}
_queue_update();
}
diff --git a/modules/noise/noise_texture_3d.cpp b/modules/noise/noise_texture_3d.cpp
index f6c67b0f2d..ed242e7faa 100644
--- a/modules/noise/noise_texture_3d.cpp
+++ b/modules/noise/noise_texture_3d.cpp
@@ -32,8 +32,6 @@
#include "noise.h"
-#include "core/core_string_names.h"
-
NoiseTexture3D::NoiseTexture3D() {
noise = Ref<Noise>();
@@ -214,11 +212,11 @@ void NoiseTexture3D::set_noise(Ref<Noise> p_noise) {
return;
}
if (noise.is_valid()) {
- noise->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update));
+ noise->disconnect_changed(callable_mp(this, &NoiseTexture3D::_queue_update));
}
noise = p_noise;
if (noise.is_valid()) {
- noise->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update));
+ noise->connect_changed(callable_mp(this, &NoiseTexture3D::_queue_update));
}
_queue_update();
}
@@ -297,11 +295,11 @@ void NoiseTexture3D::set_color_ramp(const Ref<Gradient> &p_gradient) {
return;
}
if (color_ramp.is_valid()) {
- color_ramp->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update));
+ color_ramp->disconnect_changed(callable_mp(this, &NoiseTexture3D::_queue_update));
}
color_ramp = p_gradient;
if (color_ramp.is_valid()) {
- color_ramp->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update));
+ color_ramp->connect_changed(callable_mp(this, &NoiseTexture3D::_queue_update));
}
_queue_update();
}
diff --git a/modules/noise/tests/test_noise_texture_2d.h b/modules/noise/tests/test_noise_texture_2d.h
index e2ec39ef48..938e8fd6ab 100644
--- a/modules/noise/tests/test_noise_texture_2d.h
+++ b/modules/noise/tests/test_noise_texture_2d.h
@@ -210,7 +210,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a basic noise texture with mip
noise_texture->set_generate_mipmaps(true);
Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr()));
- noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_mip_and_color_ramp));
+ noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_mip_and_color_ramp));
MessageQueue::get_singleton()->flush();
}
@@ -227,7 +227,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a normal map without mipmaps")
noise_texture->set_generate_mipmaps(false);
Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr()));
- noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_normal_map));
+ noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_normal_map));
MessageQueue::get_singleton()->flush();
}
@@ -245,7 +245,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a seamless noise texture") {
SUBCASE("Grayscale(L8) 16x16, with seamless blend skirt of 0.05") {
noise_texture->set_seamless_blend_skirt(0.05);
- noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_grayscale));
+ noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_grayscale));
MessageQueue::get_singleton()->flush();
}
@@ -257,7 +257,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a seamless noise texture") {
gradient->set_points(points);
noise_texture->set_color_ramp(gradient);
noise_texture->set_seamless_blend_skirt(1.0);
- noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_rgba));
+ noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_rgba));
MessageQueue::get_singleton()->flush();
}
}
diff --git a/modules/noise/tests/test_noise_texture_3d.h b/modules/noise/tests/test_noise_texture_3d.h
index a612f2920a..b708eac43b 100644
--- a/modules/noise/tests/test_noise_texture_3d.h
+++ b/modules/noise/tests/test_noise_texture_3d.h
@@ -194,7 +194,7 @@ TEST_CASE("[NoiseTexture3D][SceneTree] Generating a basic noise texture with mip
noise_texture->set_depth(16);
Ref<NoiseTexture3DTester> tester = memnew(NoiseTexture3DTester(noise_texture.ptr()));
- noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTexture3DTester::check_mip_and_color_ramp));
+ noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_mip_and_color_ramp));
MessageQueue::get_singleton()->flush();
}
@@ -213,7 +213,7 @@ TEST_CASE("[NoiseTexture3D][SceneTree] Generating a seamless noise texture") {
SUBCASE("Grayscale(L8) 16x16x16, with seamless blend skirt of 0.05") {
noise_texture->set_seamless_blend_skirt(0.05);
- noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_grayscale));
+ noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_grayscale));
MessageQueue::get_singleton()->flush();
}
@@ -225,7 +225,7 @@ TEST_CASE("[NoiseTexture3D][SceneTree] Generating a seamless noise texture") {
gradient->set_points(points);
noise_texture->set_color_ramp(gradient);
noise_texture->set_seamless_blend_skirt(1.0);
- noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_rgba));
+ noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_rgba));
MessageQueue::get_singleton()->flush();
}
}
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
index 6fffa1ed07..65559afed0 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
@@ -134,6 +134,10 @@ void OpenXRHandTrackingExtension::on_process() {
// process our hands
const XrTime time = OpenXRAPI::get_singleton()->get_next_frame_time(); // This data will be used for the next frame we render
+ if (time == 0) {
+ // we don't have timing info yet, or we're skipping a frame...
+ return;
+ }
XrResult result;
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index 69fbf87483..eee0de967e 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -355,41 +355,14 @@ void RaycastOcclusionCull::Scenario::_update_dirty_instance(int p_idx, RID *p_in
// Embree requires the last element to be readable by a 16-byte SSE load instruction, so we add padding to be safe.
occ_inst->xformed_vertices.resize(vertices_size + 1);
- const Vector3 *read_ptr = occ->vertices.ptr();
- Vector3 *write_ptr = occ_inst->xformed_vertices.ptr();
-
- if (vertices_size > 1024) {
- TransformThreadData td;
- td.xform = occ_inst->xform;
- td.read = read_ptr;
- td.write = write_ptr;
- td.vertex_count = vertices_size;
- td.thread_count = WorkerThreadPool::get_singleton()->get_thread_count();
- WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &Scenario::_transform_vertices_thread, &td, td.thread_count, -1, true, SNAME("RaycastOcclusionCull"));
- WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
-
- } else {
- _transform_vertices_range(read_ptr, write_ptr, occ_inst->xform, 0, vertices_size);
- }
+ for_range(0, vertices_size, vertices_size > 1024, SNAME("RaycastOcclusionCull"), [&](const int i) {
+ occ_inst->xformed_vertices[i] = occ_inst->xform.xform(occ->vertices[i]);
+ });
occ_inst->indices.resize(occ->indices.size());
memcpy(occ_inst->indices.ptr(), occ->indices.ptr(), occ->indices.size() * sizeof(int32_t));
}
-void RaycastOcclusionCull::Scenario::_transform_vertices_thread(uint32_t p_thread, TransformThreadData *p_data) {
- uint32_t vertex_total = p_data->vertex_count;
- uint32_t total_threads = p_data->thread_count;
- uint32_t from = p_thread * vertex_total / total_threads;
- uint32_t to = (p_thread + 1 == total_threads) ? vertex_total : ((p_thread + 1) * vertex_total / total_threads);
- _transform_vertices_range(p_data->read, p_data->write, p_data->xform, from, to);
-}
-
-void RaycastOcclusionCull::Scenario::_transform_vertices_range(const Vector3 *p_read, Vector3 *p_write, const Transform3D &p_xform, int p_from, int p_to) {
- for (int i = p_from; i < p_to; i++) {
- p_write[i] = p_xform.xform(p_read[i]);
- }
-}
-
void RaycastOcclusionCull::Scenario::_commit_scene(void *p_ud) {
Scenario *scenario = (Scenario *)p_ud;
int commit_idx = 1 - (scenario->current_scene_idx);
diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h
index c4e733b664..7a5346878b 100644
--- a/modules/raycast/raycast_occlusion_cull.h
+++ b/modules/raycast/raycast_occlusion_cull.h
@@ -121,14 +121,6 @@ private:
const uint32_t *masks;
};
- struct TransformThreadData {
- uint32_t thread_count;
- uint32_t vertex_count;
- Transform3D xform;
- const Vector3 *read;
- Vector3 *write = nullptr;
- };
-
Thread *commit_thread = nullptr;
bool commit_done = true;
bool dirty = false;
@@ -144,8 +136,6 @@ private:
void _update_dirty_instance_thread(int p_idx, RID *p_instances);
void _update_dirty_instance(int p_idx, RID *p_instances);
- void _transform_vertices_thread(uint32_t p_thread, TransformThreadData *p_data);
- void _transform_vertices_range(const Vector3 *p_read, Vector3 *p_write, const Transform3D &p_xform, int p_from, int p_to);
static void _commit_scene(void *p_ud);
bool update();
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index b8010e6692..13d8a2c17a 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -52,6 +52,7 @@ using namespace godot;
#include "core/object/worker_thread_pool.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
+#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index aba727edaa..44700e045b 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -85,7 +85,7 @@ using namespace godot;
#include "core/object/worker_thread_pool.h"
#include "core/templates/hash_map.h"
#include "core/templates/rid_owner.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/image_texture.h"
#include "servers/text/text_server_extension.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index d81b50779e..54311caaf9 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -83,7 +83,7 @@ using namespace godot;
#include "core/object/worker_thread_pool.h"
#include "core/templates/hash_map.h"
#include "core/templates/rid_owner.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/image_texture.h"
#include "servers/text/text_server_extension.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 6c961813b4..d964fd7627 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
+#include "scene/resources/image_texture.h"
#ifdef _MSC_VER
#pragma warning(push)
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 32adc28a88..21d4caef45 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -43,6 +43,8 @@
#include <theora/theoradec.h>
#include <vorbis/codec.h>
+class ImageTexture;
+
//#define THEORA_USE_THREAD_STREAMING
class VideoStreamPlaybackTheora : public VideoStreamPlayback {
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index fcd717cfec..b54335b724 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -33,6 +33,7 @@
#include "core/io/file_access.h"
#include "core/variant/typed_array.h"
+#include "modules/vorbis/resource_importer_ogg_vorbis.h"
#include <ogg/ogg.h>
int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
@@ -520,6 +521,9 @@ bool AudioStreamOggVorbis::is_monophonic() const {
}
void AudioStreamOggVorbis::_bind_methods() {
+ ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_buffer", "buffer"), &AudioStreamOggVorbis::load_from_buffer);
+ ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file);
+
ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOggVorbis::set_packet_sequence);
ClassDB::bind_method(D_METHOD("get_packet_sequence"), &AudioStreamOggVorbis::get_packet_sequence);
@@ -549,3 +553,11 @@ void AudioStreamOggVorbis::_bind_methods() {
AudioStreamOggVorbis::AudioStreamOggVorbis() {}
AudioStreamOggVorbis::~AudioStreamOggVorbis() {}
+
+Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_buffer(const Vector<uint8_t> &file_data) {
+ return ResourceImporterOggVorbis::load_from_buffer(file_data);
+}
+
+Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_file(const String &p_path) {
+ return ResourceImporterOggVorbis::load_from_file(p_path);
+}
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h
index c76df7f84d..41ce942eec 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -125,6 +125,8 @@ protected:
static void _bind_methods();
public:
+ static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path);
+ static Ref<AudioStreamOggVorbis> load_from_buffer(const Vector<uint8_t> &file_data);
void set_loop(bool p_enable);
virtual bool has_loop() const override;
diff --git a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml
index 4f920e2e04..7e3af6688a 100644
--- a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml
+++ b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml
@@ -1,11 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioStreamOggVorbis" inherits="AudioStream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
+ A class representing an Ogg Vorbis audio stream.
</brief_description>
<description>
+ The AudioStreamOggVorbis class is a specialized [AudioStream] for handling Ogg Vorbis file formats. It offers functionality for loading and playing back Ogg Vorbis files, as well as managing looping and other playback properties. This class is part of the audio stream system, which also supports WAV files through the [AudioStreamWAV] class.
</description>
<tutorials>
</tutorials>
+ <methods>
+ <method name="load_from_buffer" qualifiers="static">
+ <return type="AudioStreamOggVorbis" />
+ <param index="0" name="buffer" type="PackedByteArray" />
+ <description>
+ Creates a new AudioStreamOggVorbis instance from the given buffer. The buffer must contain Ogg Vorbis data.
+ </description>
+ </method>
+ <method name="load_from_file" qualifiers="static">
+ <return type="AudioStreamOggVorbis" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Creates a new AudioStreamOggVorbis instance from the given file path. The file must be in Ogg Vorbis format.
+ </description>
+ </method>
+ </methods>
<members>
<member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4">
</member>
diff --git a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
index ccedb9c98e..10c87b899f 100644
--- a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
+++ b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
@@ -6,6 +6,22 @@
</description>
<tutorials>
</tutorials>
+ <methods>
+ <method name="load_from_buffer" qualifiers="static">
+ <return type="AudioStreamOggVorbis" />
+ <param index="0" name="buffer" type="PackedByteArray" />
+ <description>
+ This method loads audio data from a PackedByteArray buffer into an AudioStreamOggVorbis object.
+ </description>
+ </method>
+ <method name="load_from_file" qualifiers="static">
+ <return type="AudioStreamOggVorbis" />
+ <param index="0" name="path" type="String" />
+ <description>
+ This method loads audio data from a file into an AudioStreamOggVorbis object. The file path is provided as a string.
+ </description>
+ </method>
+ </methods>
<members>
<member name="bar_beats" type="int" setter="" getter="" default="4">
</member>
diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp
index 028b7a3086..26af912999 100644
--- a/modules/vorbis/register_types.cpp
+++ b/modules/vorbis/register_types.cpp
@@ -31,7 +31,10 @@
#include "register_types.h"
#include "audio_stream_ogg_vorbis.h"
+
+#ifdef TOOLS_ENABLED
#include "resource_importer_ogg_vorbis.h"
+#endif
void initialize_vorbis_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp
index 8392750798..b42cd20589 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp
@@ -81,18 +81,50 @@ void ResourceImporterOggVorbis::get_import_options(const String &p_path, List<Im
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,or_greater"), 4));
}
-Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const String &p_path) {
- Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(f.is_null(), Ref<AudioStreamOggVorbis>(), "Cannot open file '" + p_path + "'.");
+#ifdef TOOLS_ENABLED
+
+bool ResourceImporterOggVorbis::has_advanced_options() const {
+ return true;
+}
+
+void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) {
+ Ref<AudioStreamOggVorbis> ogg_stream = load_from_file(p_path);
+ if (ogg_stream.is_valid()) {
+ AudioStreamImportSettings::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream);
+ }
+}
+#endif
+
+Error ResourceImporterOggVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+ bool loop = p_options["loop"];
+ float loop_offset = p_options["loop_offset"];
+ double bpm = p_options["bpm"];
+ int beat_count = p_options["beat_count"];
+ int bar_beats = p_options["bar_beats"];
- uint64_t len = f->get_length();
+ Ref<AudioStreamOggVorbis> ogg_vorbis_stream = load_from_file(p_source_file);
+ if (ogg_vorbis_stream.is_null()) {
+ return ERR_CANT_OPEN;
+ }
- Vector<uint8_t> file_data;
- file_data.resize(len);
- uint8_t *w = file_data.ptrw();
+ ogg_vorbis_stream->set_loop(loop);
+ ogg_vorbis_stream->set_loop_offset(loop_offset);
+ ogg_vorbis_stream->set_bpm(bpm);
+ ogg_vorbis_stream->set_beat_count(beat_count);
+ ogg_vorbis_stream->set_bar_beats(bar_beats);
+
+ return ResourceSaver::save(ogg_vorbis_stream, p_save_path + ".oggvorbisstr");
+}
- f->get_buffer(w, len);
+ResourceImporterOggVorbis::ResourceImporterOggVorbis() {
+}
+
+void ResourceImporterOggVorbis::_bind_methods() {
+ ClassDB::bind_static_method("ResourceImporterOggVorbis", D_METHOD("load_from_buffer", "buffer"), &ResourceImporterOggVorbis::load_from_buffer);
+ ClassDB::bind_static_method("ResourceImporterOggVorbis", D_METHOD("load_from_file", "path"), &ResourceImporterOggVorbis::load_from_file);
+}
+Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_buffer(const Vector<uint8_t> &file_data) {
Ref<AudioStreamOggVorbis> ogg_vorbis_stream;
ogg_vorbis_stream.instantiate();
@@ -114,7 +146,7 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str
err = ogg_sync_check(&sync_state);
ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
while (ogg_sync_pageout(&sync_state, &page) != 1) {
- if (cursor >= len) {
+ if (cursor >= size_t(file_data.size())) {
done = true;
break;
}
@@ -123,8 +155,8 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str
char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE);
err = ogg_sync_check(&sync_state);
ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
- ERR_FAIL_COND_V(cursor > len, Ref<AudioStreamOggVorbis>());
- size_t copy_size = len - cursor;
+ ERR_FAIL_COND_V(cursor > size_t(file_data.size()), Ref<AudioStreamOggVorbis>());
+ size_t copy_size = file_data.size() - cursor;
if (copy_size > OGG_SYNC_BUFFER_SIZE) {
copy_size = OGG_SYNC_BUFFER_SIZE;
}
@@ -201,40 +233,8 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str
return ogg_vorbis_stream;
}
-#ifdef TOOLS_ENABLED
-
-bool ResourceImporterOggVorbis::has_advanced_options() const {
- return true;
-}
-
-void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) {
- Ref<AudioStreamOggVorbis> ogg_stream = import_ogg_vorbis(p_path);
- if (ogg_stream.is_valid()) {
- AudioStreamImportSettings::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream);
- }
-}
-#endif
-
-Error ResourceImporterOggVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
- bool loop = p_options["loop"];
- float loop_offset = p_options["loop_offset"];
- double bpm = p_options["bpm"];
- int beat_count = p_options["beat_count"];
- int bar_beats = p_options["bar_beats"];
-
- Ref<AudioStreamOggVorbis> ogg_vorbis_stream = import_ogg_vorbis(p_source_file);
- if (ogg_vorbis_stream.is_null()) {
- return ERR_CANT_OPEN;
- }
-
- ogg_vorbis_stream->set_loop(loop);
- ogg_vorbis_stream->set_loop_offset(loop_offset);
- ogg_vorbis_stream->set_bpm(bpm);
- ogg_vorbis_stream->set_beat_count(beat_count);
- ogg_vorbis_stream->set_bar_beats(bar_beats);
-
- return ResourceSaver::save(ogg_vorbis_stream, p_save_path + ".oggvorbisstr");
-}
-
-ResourceImporterOggVorbis::ResourceImporterOggVorbis() {
+Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_file(const String &p_path) {
+ Vector<uint8_t> file_data = FileAccess::get_file_as_bytes(p_path);
+ ERR_FAIL_COND_V_MSG(file_data.is_empty(), Ref<AudioStreamOggVorbis>(), "Cannot open file '" + p_path + "'.");
+ return load_from_buffer(file_data);
}
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h
index 4874419834..59ae3378a0 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.h
+++ b/modules/vorbis/resource_importer_ogg_vorbis.h
@@ -42,16 +42,17 @@ class ResourceImporterOggVorbis : public ResourceImporter {
OGG_SYNC_BUFFER_SIZE = 8192,
};
-private:
- // virtual int get_samples_in_packet(Vector<uint8_t> p_packet) = 0;
-
- static Ref<AudioStreamOggVorbis> import_ogg_vorbis(const String &p_path);
+protected:
+ static void _bind_methods();
public:
#ifdef TOOLS_ENABLED
virtual bool has_advanced_options() const override;
virtual void show_advanced_options(const String &p_path) override;
#endif
+
+ static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path);
+ static Ref<AudioStreamOggVorbis> load_from_buffer(const Vector<uint8_t> &file_data);
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual String get_save_extension() const override;
virtual String get_resource_type() const override;
diff --git a/modules/webp/resource_saver_webp.cpp b/modules/webp/resource_saver_webp.cpp
index 92285e2eab..52289334f8 100644
--- a/modules/webp/resource_saver_webp.cpp
+++ b/modules/webp/resource_saver_webp.cpp
@@ -34,7 +34,7 @@
#include "core/io/file_access.h"
#include "core/io/image.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/image_texture.h"
Error ResourceSaverWebP::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Ref<ImageTexture> texture = p_resource;
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index 6d3affed15..d61d63d242 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -104,6 +104,12 @@
<member name="package/retain_data_on_uninstall" type="bool" setter="" getter="">
If [code]true[/code], when the user uninstalls an app, a prompt to keep the app's data will be shown.
</member>
+ <member name="package/show_as_launcher_app" type="bool" setter="" getter="">
+ If [code]true[/code], the user will be able to set this app as the system launcher in Android preferences.
+ </member>
+ <member name="package/show_in_android_tv" type="bool" setter="" getter="">
+ If [code]true[/code], this app will show in Android TV launcher UI.
+ </member>
<member name="package/signed" type="bool" setter="" getter="">
If [code]true[/code], package signing is enabled.
</member>
@@ -578,12 +584,6 @@
<member name="version/name" type="String" setter="" getter="">
Application version visible to the user.
</member>
- <member name="xr_features/hand_tracking" type="int" setter="" getter="">
- </member>
- <member name="xr_features/hand_tracking_frequency" type="int" setter="" getter="">
- </member>
- <member name="xr_features/passthrough" type="int" setter="" getter="">
- </member>
<member name="xr_features/xr_mode" type="int" setter="" getter="">
</member>
</members>
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 830548d801..cdffee9318 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -48,6 +48,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "main/splash.gen.h"
+#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For mono and svg.
#ifdef MODULE_SVG_ENABLED
@@ -260,30 +261,32 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
EditorExportPlatformAndroid *ea = static_cast<EditorExportPlatformAndroid *>(ud);
while (!ea->quit_request.is_set()) {
- // Check for plugins updates
+#ifndef DISABLE_DEPRECATED
+ // Check for android plugins updates
{
// Nothing to do if we already know the plugins have changed.
- if (!ea->plugins_changed.is_set()) {
+ if (!ea->android_plugins_changed.is_set()) {
Vector<PluginConfigAndroid> loaded_plugins = get_plugins();
- MutexLock lock(ea->plugins_lock);
+ MutexLock lock(ea->android_plugins_lock);
- if (ea->plugins.size() != loaded_plugins.size()) {
- ea->plugins_changed.set();
+ if (ea->android_plugins.size() != loaded_plugins.size()) {
+ ea->android_plugins_changed.set();
} else {
- for (int i = 0; i < ea->plugins.size(); i++) {
- if (ea->plugins[i].name != loaded_plugins[i].name) {
- ea->plugins_changed.set();
+ for (int i = 0; i < ea->android_plugins.size(); i++) {
+ if (ea->android_plugins[i].name != loaded_plugins[i].name) {
+ ea->android_plugins_changed.set();
break;
}
}
}
- if (ea->plugins_changed.is_set()) {
- ea->plugins = loaded_plugins;
+ if (ea->android_plugins_changed.is_set()) {
+ ea->android_plugins = loaded_plugins;
}
}
}
+#endif // DISABLE_DEPRECATED
// Check for devices updates
String adb = get_adb_path();
@@ -627,6 +630,7 @@ Vector<EditorExportPlatformAndroid::ABI> EditorExportPlatformAndroid::get_abis()
return abis;
}
+#ifndef DISABLE_DEPRECATED
/// List the gdap files in the directory specified by the p_path parameter.
Vector<String> EditorExportPlatformAndroid::list_gdap_files(const String &p_path) {
Vector<String> dir_files;
@@ -693,6 +697,7 @@ Vector<PluginConfigAndroid> EditorExportPlatformAndroid::get_enabled_plugins(con
return enabled_plugins;
}
+#endif // DISABLE_DEPRECATED
Error EditorExportPlatformAndroid::store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method) {
zip_fileinfo zipfi = get_zip_fileinfo();
@@ -827,16 +832,6 @@ void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset>
r_permissions.push_back("android.permission.INTERNET");
}
}
-
- int xr_mode_index = p_preset->get("xr_features/xr_mode");
- if (xr_mode_index == XR_MODE_OPENXR) {
- int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
- if (hand_tracking_index > XR_HAND_TRACKING_NONE) {
- if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) {
- r_permissions.push_back("com.oculus.permission.HAND_TRACKING");
- }
- }
- }
}
void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) {
@@ -860,8 +855,23 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
}
}
- manifest_text += _get_xr_features_tag(p_preset, _uses_vulkan());
- manifest_text += _get_application_tag(p_preset, _has_read_write_storage_permission(perms));
+ if (_uses_vulkan()) {
+ manifest_text += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.level\" android:required=\"false\" android:version=\"1\" />\n";
+ manifest_text += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.version\" android:required=\"true\" android:version=\"0x400003\" />\n";
+ }
+
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ for (int i = 0; i < export_plugins.size(); i++) {
+ if (export_plugins[i]->supports_platform(Ref<EditorExportPlatform>(this))) {
+ const String contents = export_plugins[i]->get_android_manifest_element_contents(Ref<EditorExportPlatform>(this), p_debug);
+ if (!contents.is_empty()) {
+ manifest_text += contents;
+ manifest_text += "\n";
+ }
+ }
+ }
+
+ manifest_text += _get_application_tag(Ref<EditorExportPlatform>(this), p_preset, _has_read_write_storage_permission(perms), p_debug);
manifest_text += "</manifest>\n";
String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
@@ -1720,7 +1730,7 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
}
} else if (p_name == "gradle_build/use_gradle_build") {
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
- String enabled_plugins_names = PluginConfigAndroid::get_plugins_names(get_enabled_plugins(Ref<EditorExportPreset>(p_preset)));
+ String enabled_plugins_names = _get_plugins_names(Ref<EditorExportPreset>(p_preset));
if (!enabled_plugins_names.is_empty() && !gradle_build_enabled) {
return TTR("\"Use Gradle Build\" must be enabled to use the plugins.");
}
@@ -1730,22 +1740,6 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
if (xr_mode_index == XR_MODE_OPENXR && !gradle_build_enabled) {
return TTR("OpenXR requires \"Use Gradle Build\" to be enabled");
}
- } else if (p_name == "xr_features/hand_tracking") {
- int xr_mode_index = p_preset->get("xr_features/xr_mode");
- int hand_tracking = p_preset->get("xr_features/hand_tracking");
- if (xr_mode_index != XR_MODE_OPENXR) {
- if (hand_tracking > XR_HAND_TRACKING_NONE) {
- return TTR("\"Hand Tracking\" is only valid when \"XR Mode\" is \"OpenXR\".");
- }
- }
- } else if (p_name == "xr_features/passthrough") {
- int xr_mode_index = p_preset->get("xr_features/xr_mode");
- int passthrough_mode = p_preset->get("xr_features/passthrough");
- if (xr_mode_index != XR_MODE_OPENXR) {
- if (passthrough_mode > XR_PASSTHROUGH_NONE) {
- return TTR("\"Passthrough\" is only valid when \"XR Mode\" is \"OpenXR\".");
- }
- }
} else if (p_name == "gradle_build/export_format") {
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
if (int(p_preset->get("gradle_build/export_format")) == EXPORT_FORMAT_AAB && !gradle_build_enabled) {
@@ -1807,12 +1801,14 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/min_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", VULKAN_MIN_SDK_VERSION)), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/target_sdk", PROPERTY_HINT_PLACEHOLDER_TEXT, vformat("%d (default)", DEFAULT_TARGET_SDK_VERSION)), "", false, true));
+#ifndef DISABLE_DEPRECATED
Vector<PluginConfigAndroid> plugins_configs = get_plugins();
for (int i = 0; i < plugins_configs.size(); i++) {
print_verbose("Found Android plugin " + plugins_configs[i].name);
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("plugins"), plugins_configs[i].name)), false));
}
- plugins_changed.clear();
+ android_plugins_changed.clear();
+#endif // DISABLE_DEPRECATED
// Android supports multiple architectures in an app bundle, so
// we expose each option as a checkbox in the export dialog.
@@ -1841,6 +1837,8 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "package/app_category", PROPERTY_HINT_ENUM, "Accessibility,Audio,Game,Image,Maps,News,Productivity,Social,Video"), APP_CATEGORY_GAME));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_in_android_tv"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/show_as_launcher_app"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
@@ -1849,9 +1847,6 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,OpenXR"), XR_MODE_REGULAR, false, true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), XR_HAND_TRACKING_NONE, false, true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking_frequency", PROPERTY_HINT_ENUM, "Low,High"), XR_HAND_TRACKING_FREQUENCY_LOW));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/passthrough", PROPERTY_HINT_ENUM, "None,Optional,Required"), XR_PASSTHROUGH_NONE, false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true));
@@ -1889,12 +1884,14 @@ Ref<Texture2D> EditorExportPlatformAndroid::get_logo() const {
}
bool EditorExportPlatformAndroid::should_update_export_options() {
- bool export_options_changed = plugins_changed.is_set();
- if (export_options_changed) {
+#ifndef DISABLE_DEPRECATED
+ if (android_plugins_changed.is_set()) {
// don't clear unless we're reporting true, to avoid race
- plugins_changed.clear();
+ android_plugins_changed.clear();
+ return true;
}
- return export_options_changed;
+#endif // DISABLE_DEPRECATED
+ return false;
}
bool EditorExportPlatformAndroid::poll_export() {
@@ -2694,6 +2691,64 @@ String EditorExportPlatformAndroid::join_abis(const Vector<EditorExportPlatformA
return ret;
}
+String EditorExportPlatformAndroid::_get_plugins_names(const Ref<EditorExportPreset> &p_preset) const {
+ Vector<String> names;
+
+#ifndef DISABLE_DEPRECATED
+ PluginConfigAndroid::get_plugins_names(get_enabled_plugins(p_preset), names);
+#endif // DISABLE_DEPRECATED
+
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ for (int i = 0; i < export_plugins.size(); i++) {
+ if (export_plugins[i]->supports_platform(Ref<EditorExportPlatform>(this))) {
+ names.push_back(export_plugins[i]->get_name());
+ }
+ }
+
+ String plugins_names = String("|").join(names);
+ return plugins_names;
+}
+
+String EditorExportPlatformAndroid::_resolve_export_plugin_android_library_path(const String &p_android_library_path) const {
+ String absolute_path;
+ if (!p_android_library_path.is_empty()) {
+ if (p_android_library_path.is_absolute_path()) {
+ absolute_path = ProjectSettings::get_singleton()->globalize_path(p_android_library_path);
+ } else {
+ const String export_plugin_absolute_path = String("res://addons/").path_join(p_android_library_path);
+ absolute_path = ProjectSettings::get_singleton()->globalize_path(export_plugin_absolute_path);
+ }
+ }
+ return absolute_path;
+}
+
+bool EditorExportPlatformAndroid::_is_clean_build_required(const Ref<EditorExportPreset> &p_preset) {
+ bool first_build = last_gradle_build_time == 0;
+ bool have_plugins_changed = false;
+
+ String plugin_names = _get_plugins_names(p_preset);
+
+ if (!first_build) {
+ have_plugins_changed = plugin_names != last_plugin_names;
+#ifndef DISABLE_DEPRECATED
+ if (!have_plugins_changed) {
+ Vector<PluginConfigAndroid> enabled_plugins = get_enabled_plugins(p_preset);
+ for (int i = 0; i < enabled_plugins.size(); i++) {
+ if (enabled_plugins.get(i).last_updated > last_gradle_build_time) {
+ have_plugins_changed = true;
+ break;
+ }
+ }
+ }
+#endif // DISABLE_DEPRECATED
+ }
+
+ last_gradle_build_time = OS::get_singleton()->get_unix_time();
+ last_plugin_names = plugin_names;
+
+ return have_plugins_changed || first_build;
+}
+
Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
int export_format = int(p_preset->get("gradle_build/export_format"));
bool should_sign = p_preset->get("package/signed");
@@ -2851,11 +2906,40 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String sign_flag = should_sign ? "true" : "false";
String zipalign_flag = "true";
+ Vector<String> android_libraries;
+ Vector<String> android_dependencies;
+ Vector<String> android_dependencies_maven_repos;
+
+#ifndef DISABLE_DEPRECATED
Vector<PluginConfigAndroid> enabled_plugins = get_enabled_plugins(p_preset);
- String local_plugins_binaries = PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_LOCAL, enabled_plugins);
- String remote_plugins_binaries = PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_REMOTE, enabled_plugins);
- String custom_maven_repos = PluginConfigAndroid::get_plugins_custom_maven_repos(enabled_plugins);
- bool clean_build_required = is_clean_build_required(enabled_plugins);
+ PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_LOCAL, enabled_plugins, android_libraries);
+ PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_REMOTE, enabled_plugins, android_dependencies);
+ PluginConfigAndroid::get_plugins_custom_maven_repos(enabled_plugins, android_dependencies_maven_repos);
+#endif // DISABLE_DEPRECATED
+
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ for (int i = 0; i < export_plugins.size(); i++) {
+ if (export_plugins[i]->supports_platform(Ref<EditorExportPlatform>(this))) {
+ PackedStringArray export_plugin_android_libraries = export_plugins[i]->get_android_libraries(Ref<EditorExportPlatform>(this), p_debug);
+ for (int k = 0; k < export_plugin_android_libraries.size(); k++) {
+ const String resolved_android_library_path = _resolve_export_plugin_android_library_path(export_plugin_android_libraries[k]);
+ if (!resolved_android_library_path.is_empty()) {
+ android_libraries.push_back(resolved_android_library_path);
+ }
+ }
+
+ PackedStringArray export_plugin_android_dependencies = export_plugins[i]->get_android_dependencies(Ref<EditorExportPlatform>(this), p_debug);
+ android_dependencies.append_array(export_plugin_android_dependencies);
+
+ PackedStringArray export_plugin_android_dependencies_maven_repos = export_plugins[i]->get_android_dependencies_maven_repos(Ref<EditorExportPlatform>(this), p_debug);
+ android_dependencies_maven_repos.append_array(export_plugin_android_dependencies_maven_repos);
+ }
+ }
+
+ bool clean_build_required = _is_clean_build_required(p_preset);
+ String combined_android_libraries = String("|").join(android_libraries);
+ String combined_android_dependencies = String("|").join(android_dependencies);
+ String combined_android_dependencies_maven_repos = String("|").join(android_dependencies_maven_repos);
List<String> cmdline;
if (clean_build_required) {
@@ -2879,9 +2963,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
cmdline.push_back("-Pexport_version_min_sdk=" + min_sdk_version); // argument to specify the min sdk.
cmdline.push_back("-Pexport_version_target_sdk=" + target_sdk_version); // argument to specify the target sdk.
cmdline.push_back("-Pexport_enabled_abis=" + enabled_abi_string); // argument to specify enabled ABIs.
- cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies.
- cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies.
- cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies.
+ cmdline.push_back("-Pplugins_local_binaries=" + combined_android_libraries); // argument to specify the list of android libraries provided by plugins.
+ cmdline.push_back("-Pplugins_remote_binaries=" + combined_android_dependencies); // argument to specify the list of android dependencies provided by plugins.
+ cmdline.push_back("-Pplugins_maven_repos=" + combined_android_dependencies_maven_repos); // argument to specify the list of maven repos for android dependencies provided by plugins.
cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned.
cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed.
cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG));
@@ -3307,7 +3391,9 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
#endif
devices_changed.set();
- plugins_changed.set();
+#ifndef DISABLE_DEPRECATED
+ android_plugins_changed.set();
+#endif // DISABLE_DEPRECATED
#ifndef ANDROID_ENABLED
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
#endif
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index 0ac0fbb10b..a2d0417c5d 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -31,7 +31,9 @@
#ifndef ANDROID_EXPORT_PLUGIN_H
#define ANDROID_EXPORT_PLUGIN_H
+#ifndef DISABLE_DEPRECATED
#include "godot_plugin_config.h"
+#endif // DISABLE_DEPRECATED
#include "core/io/zip_io.h"
#include "core/os/os.h"
@@ -81,11 +83,14 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
EditorProgress *ep = nullptr;
};
- mutable Vector<PluginConfigAndroid> plugins;
+#ifndef DISABLE_DEPRECATED
+ mutable Vector<PluginConfigAndroid> android_plugins;
+ mutable SafeFlag android_plugins_changed;
+ Mutex android_plugins_lock;
+#endif // DISABLE_DEPRECATED
String last_plugin_names;
uint64_t last_gradle_build_time = 0;
- mutable SafeFlag plugins_changed;
- Mutex plugins_lock;
+
Vector<Device> devices;
SafeFlag devices_changed;
Mutex device_lock;
@@ -128,12 +133,14 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
static Vector<ABI> get_abis();
+#ifndef DISABLE_DEPRECATED
/// List the gdap files in the directory specified by the p_path parameter.
static Vector<String> list_gdap_files(const String &p_path);
static Vector<PluginConfigAndroid> get_plugins();
static Vector<PluginConfigAndroid> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets);
+#endif // DISABLE_DEPRECATED
static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED);
@@ -224,28 +231,11 @@ public:
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
- inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) {
- String plugin_names = PluginConfigAndroid::get_plugins_names(enabled_plugins);
- bool first_build = last_gradle_build_time == 0;
- bool have_plugins_changed = false;
-
- if (!first_build) {
- have_plugins_changed = plugin_names != last_plugin_names;
- if (!have_plugins_changed) {
- for (int i = 0; i < enabled_plugins.size(); i++) {
- if (enabled_plugins.get(i).last_updated > last_gradle_build_time) {
- have_plugins_changed = true;
- break;
- }
- }
- }
- }
+ String _get_plugins_names(const Ref<EditorExportPreset> &p_preset) const;
- last_gradle_build_time = OS::get_singleton()->get_unix_time();
- last_plugin_names = plugin_names;
+ String _resolve_export_plugin_android_library_path(const String &p_android_library_path) const;
- return have_plugins_changed || first_build;
- }
+ bool _is_clean_build_required(const Ref<EditorExportPreset> &p_preset);
String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path);
diff --git a/platform/android/export/godot_plugin_config.cpp b/platform/android/export/godot_plugin_config.cpp
index b64cca3254..cdec5f55b7 100644
--- a/platform/android/export/godot_plugin_config.cpp
+++ b/platform/android/export/godot_plugin_config.cpp
@@ -30,6 +30,8 @@
#include "godot_plugin_config.h"
+#ifndef DISABLE_DEPRECATED
+
/*
* Set of prebuilt plugins.
* Currently unused, this is just for future reference:
@@ -145,10 +147,8 @@ PluginConfigAndroid PluginConfigAndroid::load_plugin_config(Ref<ConfigFile> conf
return plugin_config;
}
-String PluginConfigAndroid::get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs) {
- String plugins_binaries;
+void PluginConfigAndroid::get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result) {
if (!plugins_configs.is_empty()) {
- Vector<String> binaries;
for (int i = 0; i < plugins_configs.size(); i++) {
PluginConfigAndroid config = plugins_configs[i];
if (!config.valid_config) {
@@ -156,56 +156,44 @@ String PluginConfigAndroid::get_plugins_binaries(String binary_type, Vector<Plug
}
if (config.binary_type == binary_type) {
- binaries.push_back(config.binary);
+ r_result.push_back(config.binary);
}
if (binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL) {
- binaries.append_array(config.local_dependencies);
+ r_result.append_array(config.local_dependencies);
}
if (binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE) {
- binaries.append_array(config.remote_dependencies);
+ r_result.append_array(config.remote_dependencies);
}
}
-
- plugins_binaries = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(binaries);
}
-
- return plugins_binaries;
}
-String PluginConfigAndroid::get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs) {
- String custom_maven_repos;
+void PluginConfigAndroid::get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result) {
if (!plugins_configs.is_empty()) {
- Vector<String> repos_urls;
for (int i = 0; i < plugins_configs.size(); i++) {
PluginConfigAndroid config = plugins_configs[i];
if (!config.valid_config) {
continue;
}
- repos_urls.append_array(config.custom_maven_repos);
+ r_result.append_array(config.custom_maven_repos);
}
-
- custom_maven_repos = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(repos_urls);
}
- return custom_maven_repos;
}
-String PluginConfigAndroid::get_plugins_names(Vector<PluginConfigAndroid> plugins_configs) {
- String plugins_names;
+void PluginConfigAndroid::get_plugins_names(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result) {
if (!plugins_configs.is_empty()) {
- Vector<String> names;
for (int i = 0; i < plugins_configs.size(); i++) {
PluginConfigAndroid config = plugins_configs[i];
if (!config.valid_config) {
continue;
}
- names.push_back(config.name);
+ r_result.push_back(config.name);
}
- plugins_names = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(names);
}
-
- return plugins_names;
}
+
+#endif // DISABLE_DEPRECATED
diff --git a/platform/android/export/godot_plugin_config.h b/platform/android/export/godot_plugin_config.h
index bef00979a9..8c56d00187 100644
--- a/platform/android/export/godot_plugin_config.h
+++ b/platform/android/export/godot_plugin_config.h
@@ -31,6 +31,8 @@
#ifndef ANDROID_GODOT_PLUGIN_CONFIG_H
#define ANDROID_GODOT_PLUGIN_CONFIG_H
+#ifndef DISABLE_DEPRECATED
+
#include "core/config/project_settings.h"
#include "core/error/error_list.h"
#include "core/io/config_file.h"
@@ -67,8 +69,6 @@ struct PluginConfigAndroid {
inline static const char *BINARY_TYPE_LOCAL = "local";
inline static const char *BINARY_TYPE_REMOTE = "remote";
- inline static const char *PLUGIN_VALUE_SEPARATOR = "|";
-
// Set to true when the config file is properly loaded.
bool valid_config = false;
// Unix timestamp of last change to this plugin.
@@ -96,11 +96,13 @@ struct PluginConfigAndroid {
static PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file, const String &path);
- static String get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs);
+ static void get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result);
- static String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs);
+ static void get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result);
- static String get_plugins_names(Vector<PluginConfigAndroid> plugins_configs);
+ static void get_plugins_names(Vector<PluginConfigAndroid> plugins_configs, Vector<String> &r_result);
};
+#endif // DISABLE_DEPRECATED
+
#endif // ANDROID_GODOT_PLUGIN_CONFIG_H
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index ba4487cc4d..d0d0c34bb4 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -254,34 +254,7 @@ String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) {
return manifest_screen_sizes;
}
-String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_vulkan) {
- String manifest_xr_features;
- int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode"));
- bool uses_xr = xr_mode_index == XR_MODE_OPENXR;
- if (uses_xr) {
- int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
- if (hand_tracking_index == XR_HAND_TRACKING_OPTIONAL) {
- manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n";
- } else if (hand_tracking_index == XR_HAND_TRACKING_REQUIRED) {
- manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"true\" />\n";
- }
-
- int passthrough_mode = p_preset->get("xr_features/passthrough");
- if (passthrough_mode == XR_PASSTHROUGH_OPTIONAL) {
- manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"false\" />\n";
- } else if (passthrough_mode == XR_PASSTHROUGH_REQUIRED) {
- manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"com.oculus.feature.PASSTHROUGH\" android:required=\"true\" />\n";
- }
- }
-
- if (p_uses_vulkan) {
- manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.level\" android:required=\"false\" android:version=\"1\" />\n";
- manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vulkan.version\" android:required=\"true\" android:version=\"0x400003\" />\n";
- }
- return manifest_xr_features;
-}
-
-String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr) {
+String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug) {
String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))));
String manifest_activity_text = vformat(
" <activity android:name=\"com.godot.game.GodotApp\" "
@@ -294,40 +267,42 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr
orientation,
bool_to_string(bool(GLOBAL_GET("display/window/size/resizable"))));
- if (p_uses_xr) {
- manifest_activity_text += " <intent-filter>\n"
- " <action android:name=\"android.intent.action.MAIN\" />\n"
- " <category android:name=\"android.intent.category.LAUNCHER\" />\n"
- "\n"
- " <!-- Enable access to OpenXR on Oculus mobile devices, no-op on other Android\n"
- " platforms. -->\n"
- " <category android:name=\"com.oculus.intent.category.VR\" />\n"
- "\n"
- " <!-- OpenXR category tag to indicate the activity starts in an immersive OpenXR mode. \n"
- " See https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#android-runtime-category. -->\n"
- " <category android:name=\"org.khronos.openxr.intent.category.IMMERSIVE_HMD\" />\n"
- "\n"
- " <!-- Enable VR access on HTC Vive Focus devices. -->\n"
- " <category android:name=\"com.htc.intent.category.VRAPP\" />\n"
- " </intent-filter>\n";
- } else {
- manifest_activity_text += " <intent-filter>\n"
- " <action android:name=\"android.intent.action.MAIN\" />\n"
- " <category android:name=\"android.intent.category.LAUNCHER\" />\n"
- " </intent-filter>\n";
+ manifest_activity_text += " <intent-filter>\n"
+ " <action android:name=\"android.intent.action.MAIN\" />\n"
+ " <category android:name=\"android.intent.category.LAUNCHER\" />\n";
+
+ bool uses_leanback_category = p_preset->get("package/show_in_android_tv");
+ if (uses_leanback_category) {
+ manifest_activity_text += " <category android:name=\"android.intent.category.LEANBACK_LAUNCHER\" />\n";
+ }
+
+ bool uses_home_category = p_preset->get("package/show_as_launcher_app");
+ if (uses_home_category) {
+ manifest_activity_text += " <category android:name=\"android.intent.category.HOME\" />\n";
+ manifest_activity_text += " <category android:name=\"android.intent.category.DEFAULT\" />\n";
+ }
+
+ manifest_activity_text += " </intent-filter>\n";
+
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ for (int i = 0; i < export_plugins.size(); i++) {
+ if (export_plugins[i]->supports_platform(p_export_platform)) {
+ const String contents = export_plugins[i]->get_android_manifest_activity_element_contents(p_export_platform, p_debug);
+ if (!contents.is_empty()) {
+ manifest_activity_text += contents;
+ manifest_activity_text += "\n";
+ }
+ }
}
manifest_activity_text += " </activity>\n";
return manifest_activity_text;
}
-String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission) {
+String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission, bool p_debug) {
int app_category_index = (int)(p_preset->get("package/app_category"));
bool is_game = app_category_index == APP_CATEGORY_GAME;
- int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode"));
- bool uses_xr = xr_mode_index == XR_MODE_OPENXR;
-
String manifest_application_text = vformat(
" <application android:label=\"@string/godot_project_name_string\"\n"
" android:allowBackup=\"%s\"\n"
@@ -344,18 +319,18 @@ String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_
bool_to_string(p_preset->get("package/retain_data_on_uninstall")),
bool_to_string(p_has_read_write_storage_permission));
- if (uses_xr) {
- bool hand_tracking_enabled = (int)(p_preset->get("xr_features/hand_tracking")) > XR_HAND_TRACKING_NONE;
- if (hand_tracking_enabled) {
- int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency");
- String hand_tracking_frequency = hand_tracking_frequency_index == XR_HAND_TRACKING_FREQUENCY_LOW ? "LOW" : "HIGH";
- manifest_application_text += vformat(
- " <meta-data tools:node=\"replace\" android:name=\"com.oculus.handtracking.frequency\" android:value=\"%s\" />\n",
- hand_tracking_frequency);
- manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.handtracking.version\" android:value=\"V2.0\" />\n";
+ Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
+ for (int i = 0; i < export_plugins.size(); i++) {
+ if (export_plugins[i]->supports_platform(p_export_platform)) {
+ const String contents = export_plugins[i]->get_android_manifest_application_element_contents(p_export_platform, p_debug);
+ if (!contents.is_empty()) {
+ manifest_application_text += contents;
+ manifest_application_text += "\n";
+ }
}
}
- manifest_application_text += _get_activity_tag(p_preset, uses_xr);
+
+ manifest_application_text += _get_activity_tag(p_export_platform, p_preset, p_debug);
manifest_application_text += " </application>\n";
return manifest_application_text;
}
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index 8a885a0d12..2498394add 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -61,20 +61,6 @@ static const int APP_CATEGORY_VIDEO = 8;
static const int XR_MODE_REGULAR = 0;
static const int XR_MODE_OPENXR = 1;
-// Supported XR hand tracking modes.
-static const int XR_HAND_TRACKING_NONE = 0;
-static const int XR_HAND_TRACKING_OPTIONAL = 1;
-static const int XR_HAND_TRACKING_REQUIRED = 2;
-
-// Supported XR hand tracking frequencies.
-static const int XR_HAND_TRACKING_FREQUENCY_LOW = 0;
-static const int XR_HAND_TRACKING_FREQUENCY_HIGH = 1;
-
-// Supported XR passthrough modes.
-static const int XR_PASSTHROUGH_NONE = 0;
-static const int XR_PASSTHROUGH_OPTIONAL = 1;
-static const int XR_PASSTHROUGH_REQUIRED = 2;
-
struct CustomExportData {
String assets_directory;
bool debug;
@@ -116,10 +102,8 @@ String _get_gles_tag();
String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset);
-String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_vulkan);
-
-String _get_activity_tag(const Ref<EditorExportPreset> &p_preset, bool p_uses_xr);
+String _get_activity_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug);
-String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission);
+String _get_application_tag(const Ref<EditorExportPlatform> &p_export_platform, const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission, bool p_debug);
#endif // ANDROID_GRADLE_EXPORT_UTIL_H
diff --git a/platform/android/java/app/src/com/godot/game/GodotApp.java b/platform/android/java/app/src/com/godot/game/GodotApp.java
index 1d2cc05715..9142d767b4 100644
--- a/platform/android/java/app/src/com/godot/game/GodotApp.java
+++ b/platform/android/java/app/src/com/godot/game/GodotApp.java
@@ -30,7 +30,7 @@
package com.godot.game;
-import org.godotengine.godot.FullScreenGodotApp;
+import org.godotengine.godot.GodotActivity;
import android.os.Bundle;
@@ -38,7 +38,7 @@ import android.os.Bundle;
* Template activity for Godot Android builds.
* Feel free to extend and modify this class for your custom logic.
*/
-public class GodotApp extends FullScreenGodotApp {
+public class GodotApp extends GodotActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(R.style.GodotAppMainTheme);
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
index 64d3d4eca1..7cedfa6888 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -39,7 +39,7 @@ import android.os.*
import android.util.Log
import android.widget.Toast
import androidx.window.layout.WindowMetricsCalculator
-import org.godotengine.godot.FullScreenGodotApp
+import org.godotengine.godot.GodotActivity
import org.godotengine.godot.GodotLib
import org.godotengine.godot.utils.PermissionsUtil
import org.godotengine.godot.utils.ProcessPhoenix
@@ -55,7 +55,7 @@ import kotlin.math.min
*
* It also plays the role of the primary editor window.
*/
-open class GodotEditor : FullScreenGodotApp() {
+open class GodotEditor : GodotActivity() {
companion object {
private val TAG = GodotEditor::class.java.simpleName
@@ -115,7 +115,7 @@ open class GodotEditor : FullScreenGodotApp() {
runOnUiThread {
// Enable long press, panning and scaling gestures
- godotFragment?.renderView?.inputHandler?.apply {
+ godotFragment?.godot?.renderView?.inputHandler?.apply {
enableLongPress(longPressEnabled)
enablePanningAndScalingGestures(panScaleEnabled)
}
@@ -318,7 +318,7 @@ open class GodotEditor : FullScreenGodotApp() {
override fun onRequestPermissionsResult(
requestCode: Int,
- permissions: Array<String?>,
+ permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
diff --git a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
index 3e975449d8..91d272735e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
@@ -30,156 +30,10 @@
package org.godotengine.godot;
-import org.godotengine.godot.utils.ProcessPhoenix;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-import androidx.annotation.CallSuper;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-
/**
- * Base activity for Android apps intending to use Godot as the primary and only screen.
+ * Base abstract activity for Android apps intending to use Godot as the primary screen.
*
- * It's also a reference implementation for how to setup and use the {@link Godot} fragment
- * within an Android app.
+ * @deprecated Use {@link GodotActivity}
*/
-public abstract class FullScreenGodotApp extends FragmentActivity implements GodotHost {
- private static final String TAG = FullScreenGodotApp.class.getSimpleName();
-
- protected static final String EXTRA_FORCE_QUIT = "force_quit_requested";
- protected static final String EXTRA_NEW_LAUNCH = "new_launch_requested";
-
- @Nullable
- private Godot godotFragment;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.godot_app_layout);
-
- handleStartIntent(getIntent(), true);
-
- Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.godot_fragment_container);
- if (currentFragment instanceof Godot) {
- Log.v(TAG, "Reusing existing Godot fragment instance.");
- godotFragment = (Godot)currentFragment;
- } else {
- Log.v(TAG, "Creating new Godot fragment instance.");
- godotFragment = initGodotInstance();
- getSupportFragmentManager().beginTransaction().replace(R.id.godot_fragment_container, godotFragment).setPrimaryNavigationFragment(godotFragment).commitNowAllowingStateLoss();
- }
- }
-
- @Override
- public void onDestroy() {
- Log.v(TAG, "Destroying Godot app...");
- super.onDestroy();
- terminateGodotInstance(godotFragment);
- }
-
- @Override
- public final void onGodotForceQuit(Godot instance) {
- runOnUiThread(() -> {
- terminateGodotInstance(instance);
- });
- }
-
- private void terminateGodotInstance(Godot instance) {
- if (instance == godotFragment) {
- Log.v(TAG, "Force quitting Godot instance");
- ProcessPhoenix.forceQuit(FullScreenGodotApp.this);
- }
- }
-
- @Override
- public final void onGodotRestartRequested(Godot instance) {
- runOnUiThread(() -> {
- if (instance == godotFragment) {
- // It's very hard to properly de-initialize Godot on Android to restart the game
- // from scratch. Therefore, we need to kill the whole app process and relaunch it.
- //
- // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including
- // releasing and reloading native libs or resetting their state somehow and clearing static data).
- Log.v(TAG, "Restarting Godot instance...");
- ProcessPhoenix.triggerRebirth(FullScreenGodotApp.this);
- }
- });
- }
-
- @Override
- public void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- setIntent(intent);
-
- handleStartIntent(intent, false);
-
- if (godotFragment != null) {
- godotFragment.onNewIntent(intent);
- }
- }
-
- private void handleStartIntent(Intent intent, boolean newLaunch) {
- boolean forceQuitRequested = intent.getBooleanExtra(EXTRA_FORCE_QUIT, false);
- if (forceQuitRequested) {
- Log.d(TAG, "Force quit requested, terminating..");
- ProcessPhoenix.forceQuit(this);
- return;
- }
-
- if (!newLaunch) {
- boolean newLaunchRequested = intent.getBooleanExtra(EXTRA_NEW_LAUNCH, false);
- if (newLaunchRequested) {
- Log.d(TAG, "New launch requested, restarting..");
-
- Intent restartIntent = new Intent(intent).putExtra(EXTRA_NEW_LAUNCH, false);
- ProcessPhoenix.triggerRebirth(this, restartIntent);
- return;
- }
- }
- }
-
- @CallSuper
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (godotFragment != null) {
- godotFragment.onActivityResult(requestCode, resultCode, data);
- }
- }
-
- @CallSuper
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (godotFragment != null) {
- godotFragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
- }
- }
-
- @Override
- public void onBackPressed() {
- if (godotFragment != null) {
- godotFragment.onBackPressed();
- } else {
- super.onBackPressed();
- }
- }
-
- /**
- * Used to initialize the Godot fragment instance in {@link FullScreenGodotApp#onCreate(Bundle)}.
- */
- @NonNull
- protected Godot initGodotInstance() {
- return new Godot();
- }
-
- @Nullable
- protected final Godot getGodotFragment() {
- return godotFragment;
- }
-}
+@Deprecated
+public abstract class FullScreenGodotApp extends GodotActivity {}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
deleted file mode 100644
index 9f2dec7317..0000000000
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ /dev/null
@@ -1,1195 +0,0 @@
-/**************************************************************************/
-/* Godot.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;
-
-import static android.content.Context.MODE_PRIVATE;
-import static android.content.Context.WINDOW_SERVICE;
-
-import org.godotengine.godot.input.GodotEditText;
-import org.godotengine.godot.io.directory.DirectoryAccessHandler;
-import org.godotengine.godot.io.file.FileAccessHandler;
-import org.godotengine.godot.plugin.GodotPlugin;
-import org.godotengine.godot.plugin.GodotPluginRegistry;
-import org.godotengine.godot.tts.GodotTTS;
-import org.godotengine.godot.utils.BenchmarkUtils;
-import org.godotengine.godot.utils.GodotNetUtils;
-import org.godotengine.godot.utils.PermissionsUtil;
-import org.godotengine.godot.xr.XRMode;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.PendingIntent;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Messenger;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.util.Log;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.Surface;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.ViewTreeObserver;
-import android.view.Window;
-import android.view.WindowInsets;
-import android.view.WindowInsetsAnimation;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.annotation.CallSuper;
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.fragment.app.Fragment;
-
-import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
-import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
-import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller;
-import com.google.android.vending.expansion.downloader.Helpers;
-import com.google.android.vending.expansion.downloader.IDownloaderClient;
-import com.google.android.vending.expansion.downloader.IDownloaderService;
-import com.google.android.vending.expansion.downloader.IStub;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.security.MessageDigest;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-
-public class Godot extends Fragment implements SensorEventListener, IDownloaderClient {
- private static final String TAG = Godot.class.getSimpleName();
-
- private IStub mDownloaderClientStub;
- private TextView mStatusText;
- private TextView mProgressFraction;
- private TextView mProgressPercent;
- private TextView mAverageSpeed;
- private TextView mTimeRemaining;
- private ProgressBar mPB;
- private ClipboardManager mClipboard;
-
- private View mDashboard;
- private View mCellMessage;
-
- private Button mPauseButton;
- private Button mWiFiSettingsButton;
-
- private XRMode xrMode = XRMode.REGULAR;
- private boolean use_immersive = false;
- private boolean use_debug_opengl = false;
- private boolean mStatePaused;
- private boolean activityResumed;
- private int mState;
-
- private GodotHost godotHost;
- private GodotPluginRegistry pluginRegistry;
-
- static private Intent mCurrentIntent;
-
- public void onNewIntent(Intent intent) {
- mCurrentIntent = intent;
- }
-
- static public Intent getCurrentIntent() {
- return mCurrentIntent;
- }
-
- private void setState(int newState) {
- if (mState != newState) {
- mState = newState;
- mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState));
- }
- }
-
- private void setButtonPausedState(boolean paused) {
- mStatePaused = paused;
- int stringResourceID = paused ? R.string.text_button_resume : R.string.text_button_pause;
- mPauseButton.setText(stringResourceID);
- }
-
- private String[] command_line;
- private boolean use_apk_expansion;
-
- private ViewGroup containerLayout;
- public GodotRenderView mRenderView;
- private boolean godot_initialized = false;
-
- private SensorManager mSensorManager;
- private Sensor mAccelerometer;
- private Sensor mGravity;
- private Sensor mMagnetometer;
- private Sensor mGyroscope;
-
- public GodotIO io;
- public GodotNetUtils netUtils;
- public GodotTTS tts;
- private DirectoryAccessHandler directoryAccessHandler;
- private FileAccessHandler fileAccessHandler;
-
- public interface ResultCallback {
- void callback(int requestCode, int resultCode, Intent data);
- }
- public ResultCallback result_callback;
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- if (getParentFragment() instanceof GodotHost) {
- godotHost = (GodotHost)getParentFragment();
- } else if (getActivity() instanceof GodotHost) {
- godotHost = (GodotHost)getActivity();
- }
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
- godotHost = null;
- }
-
- @CallSuper
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (result_callback != null) {
- result_callback.callback(requestCode, resultCode, data);
- result_callback = null;
- }
-
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onMainActivityResult(requestCode, resultCode, data);
- }
- }
-
- @CallSuper
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults);
- }
-
- for (int i = 0; i < permissions.length; i++) {
- GodotLib.requestPermissionResult(permissions[i], grantResults[i] == PackageManager.PERMISSION_GRANTED);
- }
- }
-
- /**
- * Invoked on the render thread when the Godot setup is complete.
- */
- @CallSuper
- protected void onGodotSetupCompleted() {
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onGodotSetupCompleted();
- }
-
- if (godotHost != null) {
- godotHost.onGodotSetupCompleted();
- }
- }
-
- /**
- * Invoked on the render thread when the Godot main loop has started.
- */
- @CallSuper
- protected void onGodotMainLoopStarted() {
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onGodotMainLoopStarted();
- }
-
- if (godotHost != null) {
- godotHost.onGodotMainLoopStarted();
- }
- }
-
- /**
- * Used by the native code (java_godot_lib_jni.cpp) to complete initialization of the GLSurfaceView view and renderer.
- */
- @Keep
- private boolean onVideoInit() {
- final Activity activity = requireActivity();
- containerLayout = new FrameLayout(activity);
- containerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-
- // GodotEditText layout
- GodotEditText editText = new GodotEditText(activity);
- editText.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
- (int)getResources().getDimension(R.dimen.text_edit_height)));
- // ...add to FrameLayout
- containerLayout.addView(editText);
-
- tts = new GodotTTS(activity);
-
- if (!GodotLib.setup(command_line, tts)) {
- Log.e(TAG, "Unable to setup the Godot engine! Aborting...");
- alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit);
- return false;
- }
-
- if (usesVulkan()) {
- if (!meetsVulkanRequirements(activity.getPackageManager())) {
- alert(R.string.error_missing_vulkan_requirements_message, R.string.text_error_title, this::forceQuit);
- return false;
- }
- mRenderView = new GodotVulkanRenderView(activity, this);
- } else {
- // Fallback to openGl
- mRenderView = new GodotGLRenderView(activity, this, xrMode, use_debug_opengl);
- }
-
- View view = mRenderView.getView();
- containerLayout.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- editText.setView(mRenderView);
- io.setEdit(editText);
-
- // Listeners for keyboard height.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- // Report the height of virtual keyboard as it changes during the animation.
- final View decorView = activity.getWindow().getDecorView();
- decorView.setWindowInsetsAnimationCallback(new WindowInsetsAnimation.Callback(WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP) {
- int startBottom, endBottom;
- @Override
- public void onPrepare(@NonNull WindowInsetsAnimation animation) {
- startBottom = decorView.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom;
- }
-
- @NonNull
- @Override
- public WindowInsetsAnimation.Bounds onStart(@NonNull WindowInsetsAnimation animation, @NonNull WindowInsetsAnimation.Bounds bounds) {
- endBottom = decorView.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom;
- return bounds;
- }
-
- @NonNull
- @Override
- public WindowInsets onProgress(@NonNull WindowInsets windowInsets, @NonNull List<WindowInsetsAnimation> list) {
- // Find the IME animation.
- WindowInsetsAnimation imeAnimation = null;
- for (WindowInsetsAnimation animation : list) {
- if ((animation.getTypeMask() & WindowInsets.Type.ime()) != 0) {
- imeAnimation = animation;
- break;
- }
- }
- // Update keyboard height based on IME animation.
- if (imeAnimation != null) {
- float interpolatedFraction = imeAnimation.getInterpolatedFraction();
- // Linear interpolation between start and end values.
- float keyboardHeight = startBottom * (1.0f - interpolatedFraction) + endBottom * interpolatedFraction;
- GodotLib.setVirtualKeyboardHeight((int)keyboardHeight);
- }
- return windowInsets;
- }
-
- @Override
- public void onEnd(@NonNull WindowInsetsAnimation animation) {
- }
- });
- } else {
- // Infer the virtual keyboard height using visible area.
- view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
- // Don't allocate a new Rect every time the callback is called.
- final Rect visibleSize = new Rect();
-
- @Override
- public void onGlobalLayout() {
- final SurfaceView view = mRenderView.getView();
- view.getWindowVisibleDisplayFrame(visibleSize);
- final int keyboardHeight = view.getHeight() - visibleSize.bottom;
- GodotLib.setVirtualKeyboardHeight(keyboardHeight);
- }
- });
- }
-
- mRenderView.queueOnRenderThread(() -> {
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onRegisterPluginWithGodotNative();
- }
- setKeepScreenOn(Boolean.parseBoolean(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
- });
-
- // Include the returned non-null views in the Godot view hierarchy.
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- View pluginView = plugin.onMainCreate(activity);
- if (pluginView != null) {
- if (plugin.shouldBeOnTop()) {
- containerLayout.addView(pluginView);
- } else {
- containerLayout.addView(pluginView, 0);
- }
- }
- }
- return true;
- }
-
- /**
- * Returns true if `Vulkan` is used for rendering.
- */
- private boolean usesVulkan() {
- final String renderer = GodotLib.getGlobal("rendering/renderer/rendering_method");
- final String renderingDevice = GodotLib.getGlobal("rendering/rendering_device/driver");
- return ("forward_plus".equals(renderer) || "mobile".equals(renderer)) && "vulkan".equals(renderingDevice);
- }
-
- /**
- * Returns true if the device meets the base requirements for Vulkan support, false otherwise.
- */
- private boolean meetsVulkanRequirements(@Nullable PackageManager packageManager) {
- if (packageManager == null) {
- return false;
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- if (!packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL, 1)) {
- // Optional requirements.. log as warning if missing
- Log.w(TAG, "The vulkan hardware level does not meet the minimum requirement: 1");
- }
-
- // Check for api version 1.0
- return packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x400003);
- }
-
- return false;
- }
-
- public void setKeepScreenOn(final boolean p_enabled) {
- runOnUiThread(() -> {
- if (p_enabled) {
- getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- } else {
- getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- });
- }
-
- /**
- * Used by the native code (java_godot_wrapper.h) to vibrate the device.
- * @param durationMs
- */
- @SuppressLint("MissingPermission")
- @Keep
- private void vibrate(int durationMs) {
- if (durationMs > 0 && requestPermission("VIBRATE")) {
- Vibrator v = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE);
- if (v != null) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- v.vibrate(VibrationEffect.createOneShot(durationMs, VibrationEffect.DEFAULT_AMPLITUDE));
- } else {
- // deprecated in API 26
- v.vibrate(durationMs);
- }
- }
- }
- }
-
- public void restart() {
- if (godotHost != null) {
- godotHost.onGodotRestartRequested(this);
- }
- }
-
- public void alert(final String message, final String title) {
- alert(message, title, null);
- }
-
- private void alert(@StringRes int messageResId, @StringRes int titleResId, @Nullable Runnable okCallback) {
- Resources res = getResources();
- alert(res.getString(messageResId), res.getString(titleResId), okCallback);
- }
-
- private void alert(final String message, final String title, @Nullable Runnable okCallback) {
- final Activity activity = getActivity();
- runOnUiThread(() -> {
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setMessage(message).setTitle(title);
- builder.setPositiveButton(
- "OK",
- (dialog, id) -> {
- if (okCallback != null) {
- okCallback.run();
- }
- dialog.cancel();
- });
- AlertDialog dialog = builder.create();
- dialog.show();
- });
- }
-
- public int getGLESVersionCode() {
- ActivityManager am = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
- ConfigurationInfo deviceInfo = am.getDeviceConfigurationInfo();
- return deviceInfo.reqGlEsVersion;
- }
-
- @CallSuper
- protected String[] getCommandLine() {
- String[] original = parseCommandLine();
- String[] updated;
- List<String> hostCommandLine = godotHost != null ? godotHost.getCommandLine() : null;
- if (hostCommandLine == null || hostCommandLine.isEmpty()) {
- updated = original;
- } else {
- updated = Arrays.copyOf(original, original.length + hostCommandLine.size());
- for (int i = 0; i < hostCommandLine.size(); i++) {
- updated[original.length + i] = hostCommandLine.get(i);
- }
- }
- return updated;
- }
-
- private String[] parseCommandLine() {
- InputStream is;
- try {
- is = getActivity().getAssets().open("_cl_");
- byte[] len = new byte[4];
- int r = is.read(len);
- if (r < 4) {
- return new String[0];
- }
- int argc = ((int)(len[3] & 0xFF) << 24) | ((int)(len[2] & 0xFF) << 16) | ((int)(len[1] & 0xFF) << 8) | ((int)(len[0] & 0xFF));
- String[] cmdline = new String[argc];
-
- for (int i = 0; i < argc; i++) {
- r = is.read(len);
- if (r < 4) {
- return new String[0];
- }
- int strlen = ((int)(len[3] & 0xFF) << 24) | ((int)(len[2] & 0xFF) << 16) | ((int)(len[1] & 0xFF) << 8) | ((int)(len[0] & 0xFF));
- if (strlen > 65535) {
- return new String[0];
- }
- byte[] arg = new byte[strlen];
- r = is.read(arg);
- if (r == strlen) {
- cmdline[i] = new String(arg, "UTF-8");
- }
- }
- return cmdline;
- } catch (Exception e) {
- // The _cl_ file can be missing with no adverse effect
- return new String[0];
- }
- }
-
- /**
- * Used by the native code (java_godot_wrapper.h) to check whether the activity is resumed or paused.
- */
- @Keep
- private boolean isActivityResumed() {
- return activityResumed;
- }
-
- /**
- * Used by the native code (java_godot_wrapper.h) to access the Android surface.
- */
- @Keep
- private Surface getSurface() {
- return mRenderView.getView().getHolder().getSurface();
- }
-
- /**
- * Used by the native code (java_godot_wrapper.h) to access the input fallback mapping.
- * @return The input fallback mapping for the current XR mode.
- */
- @Keep
- private String getInputFallbackMapping() {
- return xrMode.inputFallbackMapping;
- }
-
- String expansion_pack_path;
-
- private void initializeGodot() {
- if (expansion_pack_path != null) {
- String[] new_cmdline;
- int cll = 0;
- if (command_line != null) {
- new_cmdline = new String[command_line.length + 2];
- cll = command_line.length;
- for (int i = 0; i < command_line.length; i++) {
- new_cmdline[i] = command_line[i];
- }
- } else {
- new_cmdline = new String[2];
- }
-
- new_cmdline[cll] = "--main-pack";
- new_cmdline[cll + 1] = expansion_pack_path;
- command_line = new_cmdline;
- }
-
- final Activity activity = getActivity();
- io = new GodotIO(activity);
- netUtils = new GodotNetUtils(activity);
- Context context = getContext();
- directoryAccessHandler = new DirectoryAccessHandler(context);
- fileAccessHandler = new FileAccessHandler(context);
- mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE);
- mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- mGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
- mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
- mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
-
- godot_initialized = GodotLib.initialize(activity,
- this,
- activity.getAssets(),
- io,
- netUtils,
- directoryAccessHandler,
- fileAccessHandler,
- use_apk_expansion);
-
- result_callback = null;
- }
-
- @Override
- public void onServiceConnected(Messenger m) {
- IDownloaderService remoteService = DownloaderServiceMarshaller.CreateProxy(m);
- remoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
- }
-
- @Override
- public void onCreate(Bundle icicle) {
- BenchmarkUtils.beginBenchmarkMeasure("Godot::onCreate");
- super.onCreate(icicle);
-
- final Activity activity = getActivity();
- Window window = activity.getWindow();
- window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
- mClipboard = (ClipboardManager)activity.getSystemService(Context.CLIPBOARD_SERVICE);
- pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this);
-
- // check for apk expansion API
- boolean md5mismatch = false;
- command_line = getCommandLine();
- String main_pack_md5 = null;
- String main_pack_key = null;
-
- List<String> new_args = new LinkedList<>();
-
- for (int i = 0; i < command_line.length; i++) {
- boolean has_extra = i < command_line.length - 1;
- if (command_line[i].equals(XRMode.REGULAR.cmdLineArg)) {
- xrMode = XRMode.REGULAR;
- } else if (command_line[i].equals(XRMode.OPENXR.cmdLineArg)) {
- xrMode = XRMode.OPENXR;
- } else if (command_line[i].equals("--debug_opengl")) {
- use_debug_opengl = true;
- } else if (command_line[i].equals("--use_immersive")) {
- use_immersive = true;
- window.getDecorView().setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar
- View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
- UiChangeListener();
- } else if (command_line[i].equals("--use_apk_expansion")) {
- use_apk_expansion = true;
- } else if (has_extra && command_line[i].equals("--apk_expansion_md5")) {
- main_pack_md5 = command_line[i + 1];
- i++;
- } else if (has_extra && command_line[i].equals("--apk_expansion_key")) {
- main_pack_key = command_line[i + 1];
- SharedPreferences prefs = activity.getSharedPreferences("app_data_keys",
- MODE_PRIVATE);
- Editor editor = prefs.edit();
- editor.putString("store_public_key", main_pack_key);
-
- editor.apply();
- i++;
- } else if (command_line[i].equals("--benchmark")) {
- BenchmarkUtils.setUseBenchmark(true);
- new_args.add(command_line[i]);
- } else if (has_extra && command_line[i].equals("--benchmark-file")) {
- BenchmarkUtils.setUseBenchmark(true);
- new_args.add(command_line[i]);
-
- // Retrieve the filepath
- BenchmarkUtils.setBenchmarkFile(command_line[i + 1]);
- new_args.add(command_line[i + 1]);
-
- i++;
- } else if (command_line[i].trim().length() != 0) {
- new_args.add(command_line[i]);
- }
- }
-
- if (new_args.isEmpty()) {
- command_line = null;
- } else {
- command_line = new_args.toArray(new String[new_args.size()]);
- }
- if (use_apk_expansion && main_pack_md5 != null && main_pack_key != null) {
- // check that environment is ok!
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- // show popup and die
- }
-
- // Build the full path to the app's expansion files
- try {
- expansion_pack_path = Helpers.getSaveFilePath(getContext());
- expansion_pack_path += "/main." + activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0).versionCode + "." + activity.getPackageName() + ".obb";
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- File f = new File(expansion_pack_path);
-
- boolean pack_valid = true;
-
- if (!f.exists()) {
- pack_valid = false;
-
- } else if (obbIsCorrupted(expansion_pack_path, main_pack_md5)) {
- pack_valid = false;
- try {
- f.delete();
- } catch (Exception e) {
- }
- }
-
- if (!pack_valid) {
- Intent notifierIntent = new Intent(activity, activity.getClass());
- notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
- PendingIntent pendingIntent;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- pendingIntent = PendingIntent.getActivity(activity, 0,
- notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- } else {
- pendingIntent = PendingIntent.getActivity(activity, 0,
- notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- int startResult;
- try {
- startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
- getContext(),
- pendingIntent,
- GodotDownloaderService.class);
-
- if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
- // This is where you do set up to display the download
- // progress (next step in onCreateView)
- mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
- GodotDownloaderService.class);
-
- return;
- }
- } catch (NameNotFoundException e) {
- // TODO Auto-generated catch block
- }
- }
- }
-
- mCurrentIntent = activity.getIntent();
-
- initializeGodot();
- BenchmarkUtils.endBenchmarkMeasure("Godot::onCreate");
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) {
- if (mDownloaderClientStub != null) {
- View downloadingExpansionView =
- inflater.inflate(R.layout.downloading_expansion, container, false);
- mPB = (ProgressBar)downloadingExpansionView.findViewById(R.id.progressBar);
- mStatusText = (TextView)downloadingExpansionView.findViewById(R.id.statusText);
- mProgressFraction = (TextView)downloadingExpansionView.findViewById(R.id.progressAsFraction);
- mProgressPercent = (TextView)downloadingExpansionView.findViewById(R.id.progressAsPercentage);
- mAverageSpeed = (TextView)downloadingExpansionView.findViewById(R.id.progressAverageSpeed);
- mTimeRemaining = (TextView)downloadingExpansionView.findViewById(R.id.progressTimeRemaining);
- mDashboard = downloadingExpansionView.findViewById(R.id.downloaderDashboard);
- mCellMessage = downloadingExpansionView.findViewById(R.id.approveCellular);
- mPauseButton = (Button)downloadingExpansionView.findViewById(R.id.pauseButton);
- mWiFiSettingsButton = (Button)downloadingExpansionView.findViewById(R.id.wifiSettingsButton);
-
- return downloadingExpansionView;
- }
-
- return containerLayout;
- }
-
- @Override
- public void onDestroy() {
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onMainDestroy();
- }
-
- GodotLib.ondestroy();
-
- super.onDestroy();
-
- forceQuit();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- activityResumed = false;
-
- if (!godot_initialized) {
- if (null != mDownloaderClientStub) {
- mDownloaderClientStub.disconnect(getActivity());
- }
- return;
- }
- mRenderView.onActivityPaused();
-
- mSensorManager.unregisterListener(this);
-
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onMainPause();
- }
- }
-
- public boolean hasClipboard() {
- return mClipboard.hasPrimaryClip();
- }
-
- public String getClipboard() {
- ClipData clipData = mClipboard.getPrimaryClip();
- if (clipData == null)
- return "";
- CharSequence text = clipData.getItemAt(0).getText();
- if (text == null)
- return "";
- return text.toString();
- }
-
- public void setClipboard(String p_text) {
- ClipData clip = ClipData.newPlainText("myLabel", p_text);
- mClipboard.setPrimaryClip(clip);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- activityResumed = true;
- if (!godot_initialized) {
- if (null != mDownloaderClientStub) {
- mDownloaderClientStub.connect(getActivity());
- }
- return;
- }
-
- mRenderView.onActivityResumed();
-
- mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
- mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME);
- mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
- mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
-
- if (use_immersive) {
- Window window = getActivity().getWindow();
- window.getDecorView().setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar
- View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
- }
-
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- plugin.onMainResume();
- }
- }
-
- public void UiChangeListener() {
- final View decorView = getActivity().getWindow().getDecorView();
- decorView.setOnSystemUiVisibilityChangeListener(visibility -> {
- if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
- decorView.setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_FULLSCREEN |
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
- }
- });
- }
-
- public float[] getRotatedValues(float values[]) {
- if (values == null || values.length != 3) {
- return values;
- }
-
- Display display =
- ((WindowManager)getActivity().getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
- int displayRotation = display.getRotation();
-
- float[] rotatedValues = new float[3];
- switch (displayRotation) {
- case Surface.ROTATION_0:
- rotatedValues[0] = values[0];
- rotatedValues[1] = values[1];
- rotatedValues[2] = values[2];
- break;
- case Surface.ROTATION_90:
- rotatedValues[0] = -values[1];
- rotatedValues[1] = values[0];
- rotatedValues[2] = values[2];
- break;
- case Surface.ROTATION_180:
- rotatedValues[0] = -values[0];
- rotatedValues[1] = -values[1];
- rotatedValues[2] = values[2];
- break;
- case Surface.ROTATION_270:
- rotatedValues[0] = values[1];
- rotatedValues[1] = -values[0];
- rotatedValues[2] = values[2];
- break;
- }
-
- return rotatedValues;
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (mRenderView == null) {
- return;
- }
-
- final int typeOfSensor = event.sensor.getType();
- switch (typeOfSensor) {
- case Sensor.TYPE_ACCELEROMETER: {
- float[] rotatedValues = getRotatedValues(event.values);
- mRenderView.queueOnRenderThread(() -> {
- GodotLib.accelerometer(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]);
- });
- break;
- }
- case Sensor.TYPE_GRAVITY: {
- float[] rotatedValues = getRotatedValues(event.values);
- mRenderView.queueOnRenderThread(() -> {
- GodotLib.gravity(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]);
- });
- break;
- }
- case Sensor.TYPE_MAGNETIC_FIELD: {
- float[] rotatedValues = getRotatedValues(event.values);
- mRenderView.queueOnRenderThread(() -> {
- GodotLib.magnetometer(-rotatedValues[0], -rotatedValues[1], -rotatedValues[2]);
- });
- break;
- }
- case Sensor.TYPE_GYROSCOPE: {
- float[] rotatedValues = getRotatedValues(event.values);
- mRenderView.queueOnRenderThread(() -> {
- GodotLib.gyroscope(rotatedValues[0], rotatedValues[1], rotatedValues[2]);
- });
- break;
- }
- }
- }
-
- @Override
- public final void onAccuracyChanged(Sensor sensor, int accuracy) {
- // Do something here if sensor accuracy changes.
- }
-
- public void onBackPressed() {
- boolean shouldQuit = true;
-
- for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- if (plugin.onMainBackPressed()) {
- shouldQuit = false;
- }
- }
-
- if (shouldQuit && mRenderView != null) {
- mRenderView.queueOnRenderThread(GodotLib::back);
- }
- }
-
- /**
- * Queue a runnable to be run on the render thread.
- * <p>
- * This must be called after the render thread has started.
- */
- public final void runOnRenderThread(@NonNull Runnable action) {
- if (mRenderView != null) {
- mRenderView.queueOnRenderThread(action);
- }
- }
-
- public final void runOnUiThread(@NonNull Runnable action) {
- if (getActivity() != null) {
- getActivity().runOnUiThread(action);
- }
- }
-
- private void forceQuit() {
- // TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each
- // native Godot components that is started in Godot#onVideoInit.
- forceQuit(0);
- }
-
- @Keep
- private boolean forceQuit(int instanceId) {
- if (godotHost == null) {
- return false;
- }
- if (instanceId == 0) {
- godotHost.onGodotForceQuit(this);
- return true;
- } else {
- return godotHost.onGodotForceQuit(instanceId);
- }
- }
-
- private boolean obbIsCorrupted(String f, String main_pack_md5) {
- try {
- InputStream fis = new FileInputStream(f);
-
- // Create MD5 Hash
- byte[] buffer = new byte[16384];
-
- MessageDigest complete = MessageDigest.getInstance("MD5");
- int numRead;
- do {
- numRead = fis.read(buffer);
- if (numRead > 0) {
- complete.update(buffer, 0, numRead);
- }
- } while (numRead != -1);
-
- fis.close();
- byte[] messageDigest = complete.digest();
-
- // Create Hex String
- StringBuilder hexString = new StringBuilder();
- for (byte b : messageDigest) {
- String s = Integer.toHexString(0xFF & b);
- if (s.length() == 1) {
- s = "0" + s;
- }
- hexString.append(s);
- }
- String md5str = hexString.toString();
-
- if (!md5str.equals(main_pack_md5)) {
- return true;
- }
- return false;
- } catch (Exception e) {
- e.printStackTrace();
- return true;
- }
- }
-
- public boolean requestPermission(String p_name) {
- return PermissionsUtil.requestPermission(p_name, getActivity());
- }
-
- public boolean requestPermissions() {
- return PermissionsUtil.requestManifestPermissions(getActivity());
- }
-
- public String[] getGrantedPermissions() {
- return PermissionsUtil.getGrantedPermissions(getActivity());
- }
-
- @Keep
- private String getCACertificates() {
- return GodotNetUtils.getCACertificates();
- }
-
- /**
- * The download state should trigger changes in the UI --- it may be useful
- * to show the state as being indeterminate at times. This sample can be
- * considered a guideline.
- */
- @Override
- public void onDownloadStateChanged(int newState) {
- setState(newState);
- boolean showDashboard = true;
- boolean showCellMessage = false;
- boolean paused;
- boolean indeterminate;
- switch (newState) {
- case IDownloaderClient.STATE_IDLE:
- // STATE_IDLE means the service is listening, so it's
- // safe to start making remote service calls.
- paused = false;
- indeterminate = true;
- break;
- case IDownloaderClient.STATE_CONNECTING:
- case IDownloaderClient.STATE_FETCHING_URL:
- showDashboard = true;
- paused = false;
- indeterminate = true;
- break;
- case IDownloaderClient.STATE_DOWNLOADING:
- paused = false;
- showDashboard = true;
- indeterminate = false;
- break;
-
- case IDownloaderClient.STATE_FAILED_CANCELED:
- case IDownloaderClient.STATE_FAILED:
- case IDownloaderClient.STATE_FAILED_FETCHING_URL:
- case IDownloaderClient.STATE_FAILED_UNLICENSED:
- paused = true;
- showDashboard = false;
- indeterminate = false;
- break;
- case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
- case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
- showDashboard = false;
- paused = true;
- indeterminate = false;
- showCellMessage = true;
- break;
-
- case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
- paused = true;
- indeterminate = false;
- break;
- case IDownloaderClient.STATE_PAUSED_ROAMING:
- case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
- paused = true;
- indeterminate = false;
- break;
- case IDownloaderClient.STATE_COMPLETED:
- showDashboard = false;
- paused = false;
- indeterminate = false;
- initializeGodot();
- return;
- default:
- paused = true;
- indeterminate = true;
- showDashboard = true;
- }
- int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE;
- if (mDashboard.getVisibility() != newDashboardVisibility) {
- mDashboard.setVisibility(newDashboardVisibility);
- }
- int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE;
- if (mCellMessage.getVisibility() != cellMessageVisibility) {
- mCellMessage.setVisibility(cellMessageVisibility);
- }
-
- mPB.setIndeterminate(indeterminate);
- setButtonPausedState(paused);
- }
-
- @Override
- public void onDownloadProgress(DownloadProgressInfo progress) {
- mAverageSpeed.setText(getString(R.string.kilobytes_per_second,
- Helpers.getSpeedString(progress.mCurrentSpeed)));
- mTimeRemaining.setText(getString(R.string.time_remaining,
- Helpers.getTimeRemaining(progress.mTimeRemaining)));
-
- mPB.setMax((int)(progress.mOverallTotal >> 8));
- mPB.setProgress((int)(progress.mOverallProgress >> 8));
- mProgressPercent.setText(String.format(Locale.ENGLISH, "%d %%", progress.mOverallProgress * 100 / progress.mOverallTotal));
- mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress,
- progress.mOverallTotal));
- }
-
- public void initInputDevices() {
- mRenderView.initInputDevices();
- }
-
- @Keep
- public GodotRenderView getRenderView() { // used by native side to get renderView
- return mRenderView;
- }
-
- @Keep
- public DirectoryAccessHandler getDirectoryAccessHandler() {
- return directoryAccessHandler;
- }
-
- @Keep
- public FileAccessHandler getFileAccessHandler() {
- return fileAccessHandler;
- }
-
- @Keep
- private int createNewGodotInstance(String[] args) {
- if (godotHost != null) {
- return godotHost.onNewGodotInstanceRequested(args);
- }
- return 0;
- }
-
- @Keep
- private void beginBenchmarkMeasure(String label) {
- BenchmarkUtils.beginBenchmarkMeasure(label);
- }
-
- @Keep
- private void endBenchmarkMeasure(String label) {
- BenchmarkUtils.endBenchmarkMeasure(label);
- }
-
- @Keep
- private void dumpBenchmark(String benchmarkFile) {
- BenchmarkUtils.dumpBenchmark(fileAccessHandler, benchmarkFile);
- }
-}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
new file mode 100644
index 0000000000..23de01a191
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -0,0 +1,965 @@
+/**************************************************************************/
+/* Godot.kt */
+/**************************************************************************/
+/* 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
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.app.AlertDialog
+import android.content.*
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.graphics.Rect
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import android.os.*
+import android.util.Log
+import android.view.*
+import android.view.ViewTreeObserver.OnGlobalLayoutListener
+import android.widget.FrameLayout
+import androidx.annotation.Keep
+import androidx.annotation.StringRes
+import com.google.android.vending.expansion.downloader.*
+import org.godotengine.godot.input.GodotEditText
+import org.godotengine.godot.io.directory.DirectoryAccessHandler
+import org.godotengine.godot.io.file.FileAccessHandler
+import org.godotengine.godot.plugin.GodotPluginRegistry
+import org.godotengine.godot.tts.GodotTTS
+import org.godotengine.godot.utils.GodotNetUtils
+import org.godotengine.godot.utils.PermissionsUtil
+import org.godotengine.godot.utils.PermissionsUtil.requestPermission
+import org.godotengine.godot.utils.beginBenchmarkMeasure
+import org.godotengine.godot.utils.benchmarkFile
+import org.godotengine.godot.utils.dumpBenchmark
+import org.godotengine.godot.utils.endBenchmarkMeasure
+import org.godotengine.godot.utils.useBenchmark
+import org.godotengine.godot.xr.XRMode
+import java.io.File
+import java.io.FileInputStream
+import java.io.InputStream
+import java.nio.charset.StandardCharsets
+import java.security.MessageDigest
+import java.util.*
+
+/**
+ * Core component used to interface with the native layer of the engine.
+ *
+ * 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 {
+
+ private companion object {
+ private val TAG = Godot::class.java.simpleName
+ }
+
+ private val pluginRegistry: GodotPluginRegistry by lazy {
+ GodotPluginRegistry.initializePluginRegistry(this)
+ }
+ private val mSensorManager: SensorManager by lazy {
+ requireActivity().getSystemService(Context.SENSOR_SERVICE) as SensorManager
+ }
+ private val mAccelerometer: Sensor by lazy {
+ mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
+ }
+ private val mGravity: Sensor by lazy {
+ mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
+ }
+ private val mMagnetometer: Sensor by lazy {
+ mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
+ }
+ private val mGyroscope: Sensor by lazy {
+ mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
+ }
+ private val mClipboard: ClipboardManager by lazy {
+ requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+ }
+
+ private val uiChangeListener = View.OnSystemUiVisibilityChangeListener { visibility: Int ->
+ if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
+ val decorView = requireActivity().window.decorView
+ decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ }}
+
+ val tts = GodotTTS(context)
+ val directoryAccessHandler = DirectoryAccessHandler(context)
+ val fileAccessHandler = FileAccessHandler(context)
+ val netUtils = GodotNetUtils(context)
+
+ /**
+ * Tracks whether [onCreate] was completed successfully.
+ */
+ private var initializationStarted = false
+
+ /**
+ * Tracks whether [GodotLib.initialize] was completed successfully.
+ */
+ private var nativeLayerInitializeCompleted = false
+
+ /**
+ * Tracks whether [GodotLib.setup] was completed successfully.
+ */
+ private var nativeLayerSetupCompleted = false
+
+ /**
+ * Tracks whether [onInitRenderView] was completed successfully.
+ */
+ private var renderViewInitialized = false
+ private var primaryHost: GodotHost? = null
+
+ var io: GodotIO? = null
+
+ private var commandLine : MutableList<String> = ArrayList<String>()
+ private var xrMode = XRMode.REGULAR
+ private var expansionPackPath: String = ""
+ private var useApkExpansion = false
+ private var useImmersive = false
+ private var useDebugOpengl = false
+
+ private var containerLayout: FrameLayout? = null
+ var renderView: GodotRenderView? = null
+
+ /**
+ * Returns true if the native engine has been initialized through [onInitNativeLayer], false otherwise.
+ */
+ private fun isNativeInitialized() = nativeLayerInitializeCompleted && nativeLayerSetupCompleted
+
+ /**
+ * Returns true if the engine has been initialized, false otherwise.
+ */
+ fun isInitialized() = initializationStarted && isNativeInitialized() && renderViewInitialized
+
+ /**
+ * Provides access to the primary host [Activity]
+ */
+ fun getActivity() = primaryHost?.activity
+ private fun requireActivity() = getActivity() ?: throw IllegalStateException("Host activity must be non-null")
+
+ /**
+ * Start initialization of the Godot engine.
+ *
+ * This must be followed by [onInitNativeLayer] and [onInitRenderView] in that order to complete
+ * initialization of the engine.
+ *
+ * @throws IllegalArgumentException exception if the specified expansion pack (if any)
+ * is invalid.
+ */
+ fun onCreate(primaryHost: GodotHost) {
+ if (this.primaryHost != null || initializationStarted) {
+ Log.d(TAG, "OnCreate already invoked")
+ return
+ }
+
+ beginBenchmarkMeasure("Godot::onCreate")
+ try {
+ this.primaryHost = primaryHost
+ val activity = requireActivity()
+ val window = activity.window
+ window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
+ GodotPluginRegistry.initializePluginRegistry(this)
+ if (io == null) {
+ io = GodotIO(activity)
+ }
+
+ // check for apk expansion API
+ commandLine = getCommandLine()
+ var mainPackMd5: String? = null
+ var mainPackKey: String? = null
+ val newArgs: MutableList<String> = ArrayList()
+ var i = 0
+ while (i < commandLine.size) {
+ val hasExtra: Boolean = i < commandLine.size - 1
+ if (commandLine[i] == XRMode.REGULAR.cmdLineArg) {
+ xrMode = XRMode.REGULAR
+ } else if (commandLine[i] == XRMode.OPENXR.cmdLineArg) {
+ xrMode = XRMode.OPENXR
+ } else if (commandLine[i] == "--debug_opengl") {
+ useDebugOpengl = true
+ } else if (commandLine[i] == "--use_immersive") {
+ useImmersive = true
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or // hide nav bar
+ View.SYSTEM_UI_FLAG_FULLSCREEN or // hide status bar
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ registerUiChangeListener()
+ } else if (commandLine[i] == "--use_apk_expansion") {
+ useApkExpansion = true
+ } else if (hasExtra && commandLine[i] == "--apk_expansion_md5") {
+ mainPackMd5 = commandLine[i + 1]
+ i++
+ } else if (hasExtra && commandLine[i] == "--apk_expansion_key") {
+ mainPackKey = commandLine[i + 1]
+ val prefs = activity.getSharedPreferences(
+ "app_data_keys",
+ Context.MODE_PRIVATE
+ )
+ val editor = prefs.edit()
+ editor.putString("store_public_key", mainPackKey)
+ editor.apply()
+ i++
+ } else if (commandLine[i] == "--benchmark") {
+ useBenchmark = true
+ newArgs.add(commandLine[i])
+ } else if (hasExtra && commandLine[i] == "--benchmark-file") {
+ useBenchmark = true
+ newArgs.add(commandLine[i])
+
+ // Retrieve the filepath
+ benchmarkFile = commandLine[i + 1]
+ newArgs.add(commandLine[i + 1])
+
+ i++
+ } else if (commandLine[i].trim().isNotEmpty()) {
+ newArgs.add(commandLine[i])
+ }
+ i++
+ }
+ if (newArgs.isEmpty()) {
+ commandLine = mutableListOf()
+ } else {
+ commandLine = newArgs
+ }
+ if (useApkExpansion && mainPackMd5 != null && mainPackKey != null) {
+ // Build the full path to the app's expansion files
+ try {
+ expansionPackPath = Helpers.getSaveFilePath(context)
+ expansionPackPath += "/main." + activity.packageManager.getPackageInfo(
+ activity.packageName,
+ 0
+ ).versionCode + "." + activity.packageName + ".obb"
+ } catch (e: java.lang.Exception) {
+ Log.e(TAG, "Unable to build full path to the app's expansion files", e)
+ }
+ val f = File(expansionPackPath)
+ var packValid = true
+ if (!f.exists()) {
+ packValid = false
+ } else if (obbIsCorrupted(expansionPackPath, mainPackMd5)) {
+ packValid = false
+ try {
+ f.delete()
+ } catch (_: java.lang.Exception) {
+ }
+ }
+ if (!packValid) {
+ // Aborting engine initialization
+ throw IllegalArgumentException("Invalid expansion pack")
+ }
+ }
+
+ initializationStarted = true
+ } catch (e: java.lang.Exception) {
+ // Clear the primary host and rethrow
+ this.primaryHost = null
+ initializationStarted = false
+ throw e
+ } finally {
+ endBenchmarkMeasure("Godot::onCreate");
+ }
+ }
+
+ /**
+ * Initializes the native layer of the Godot engine.
+ *
+ * This must be preceded by [onCreate] and followed by [onInitRenderView] to complete
+ * initialization of the engine.
+ *
+ * @return false if initialization of the native layer fails, true otherwise.
+ *
+ * @throws IllegalStateException if [onCreate] has not been called.
+ */
+ fun onInitNativeLayer(host: GodotHost): Boolean {
+ if (!initializationStarted) {
+ throw IllegalStateException("OnCreate must be invoked successfully prior to initializing the native layer")
+ }
+ if (isNativeInitialized()) {
+ Log.d(TAG, "OnInitNativeLayer already invoked")
+ return true
+ }
+ if (host != primaryHost) {
+ Log.e(TAG, "Native initialization is only supported for the primary host")
+ return false
+ }
+
+ if (expansionPackPath.isNotEmpty()) {
+ commandLine.add("--main-pack")
+ commandLine.add(expansionPackPath)
+ }
+ val activity = requireActivity()
+ if (!nativeLayerInitializeCompleted) {
+ nativeLayerInitializeCompleted = GodotLib.initialize(
+ activity,
+ this,
+ activity.assets,
+ io,
+ netUtils,
+ directoryAccessHandler,
+ fileAccessHandler,
+ useApkExpansion,
+ )
+ }
+
+ if (nativeLayerInitializeCompleted && !nativeLayerSetupCompleted) {
+ nativeLayerSetupCompleted = GodotLib.setup(commandLine.toTypedArray(), tts)
+ if (!nativeLayerSetupCompleted) {
+ Log.e(TAG, "Unable to setup the Godot engine! Aborting...")
+ alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit)
+ }
+ }
+ return isNativeInitialized()
+ }
+
+ /**
+ * Used to complete initialization of the view used by the engine for rendering.
+ *
+ * This must be preceded by [onCreate] and [onInitNativeLayer] in that order to properly
+ * initialize the engine.
+ *
+ * @param host The [GodotHost] that's initializing the render views
+ * @param providedContainerLayout Optional argument; if provided, this is reused to host the Godot's render views
+ *
+ * @return A [FrameLayout] instance containing Godot's render views if initialization is successful, null otherwise.
+ *
+ * @throws IllegalStateException if [onInitNativeLayer] has not been called
+ */
+ @JvmOverloads
+ fun onInitRenderView(host: GodotHost, providedContainerLayout: FrameLayout = FrameLayout(host.activity)): FrameLayout? {
+ if (!isNativeInitialized()) {
+ throw IllegalStateException("onInitNativeLayer() must be invoked successfully prior to initializing the render view")
+ }
+
+ try {
+ val activity: Activity = host.activity
+ containerLayout = providedContainerLayout
+ containerLayout?.removeAllViews()
+ containerLayout?.layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+
+ // GodotEditText layout
+ val editText = GodotEditText(activity)
+ editText.layoutParams =
+ ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ activity.resources.getDimension(R.dimen.text_edit_height).toInt()
+ )
+ // ...add to FrameLayout
+ containerLayout?.addView(editText)
+ renderView = if (usesVulkan()) {
+ if (!meetsVulkanRequirements(activity.packageManager)) {
+ alert(R.string.error_missing_vulkan_requirements_message, R.string.text_error_title, this::forceQuit)
+ return null
+ }
+ GodotVulkanRenderView(host, this)
+ } else {
+ // Fallback to openGl
+ GodotGLRenderView(host, this, xrMode, useDebugOpengl)
+ }
+ if (host == primaryHost) {
+ renderView!!.startRenderer()
+ }
+ val view: View = renderView!!.view
+ containerLayout?.addView(
+ view,
+ ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ )
+ editText.setView(renderView)
+ io?.setEdit(editText)
+
+ // Listeners for keyboard height.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // Report the height of virtual keyboard as it changes during the animation.
+ val decorView = activity.window.decorView
+ decorView.setWindowInsetsAnimationCallback(object : WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ var startBottom = 0
+ var endBottom = 0
+ override fun onPrepare(animation: WindowInsetsAnimation) {
+ startBottom = decorView.rootWindowInsets.getInsets(WindowInsets.Type.ime()).bottom
+ }
+
+ override fun onStart(animation: WindowInsetsAnimation, bounds: WindowInsetsAnimation.Bounds): WindowInsetsAnimation.Bounds {
+ endBottom = decorView.rootWindowInsets.getInsets(WindowInsets.Type.ime()).bottom
+ return bounds
+ }
+
+ override fun onProgress(windowInsets: WindowInsets, list: List<WindowInsetsAnimation>): WindowInsets {
+ // Find the IME animation.
+ var imeAnimation: WindowInsetsAnimation? = null
+ for (animation in list) {
+ if (animation.typeMask and WindowInsets.Type.ime() != 0) {
+ imeAnimation = animation
+ break
+ }
+ }
+ // Update keyboard height based on IME animation.
+ if (imeAnimation != null) {
+ val interpolatedFraction = imeAnimation.interpolatedFraction
+ // Linear interpolation between start and end values.
+ val keyboardHeight = startBottom * (1.0f - interpolatedFraction) + endBottom * interpolatedFraction
+ GodotLib.setVirtualKeyboardHeight(keyboardHeight.toInt())
+ }
+ return windowInsets
+ }
+
+ override fun onEnd(animation: WindowInsetsAnimation) {}
+ })
+ } else {
+ // Infer the virtual keyboard height using visible area.
+ view.viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
+ // Don't allocate a new Rect every time the callback is called.
+ val visibleSize = Rect()
+ override fun onGlobalLayout() {
+ val surfaceView = renderView!!.view
+ surfaceView.getWindowVisibleDisplayFrame(visibleSize)
+ val keyboardHeight = surfaceView.height - visibleSize.bottom
+ GodotLib.setVirtualKeyboardHeight(keyboardHeight)
+ }
+ })
+ }
+
+ if (host == primaryHost) {
+ renderView!!.queueOnRenderThread {
+ for (plugin in pluginRegistry.allPlugins) {
+ plugin.onRegisterPluginWithGodotNative()
+ }
+ setKeepScreenOn(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")))
+ }
+
+ // Include the returned non-null views in the Godot view hierarchy.
+ for (plugin in pluginRegistry.allPlugins) {
+ val pluginView = plugin.onMainCreate(activity)
+ if (pluginView != null) {
+ if (plugin.shouldBeOnTop()) {
+ containerLayout?.addView(pluginView)
+ } else {
+ containerLayout?.addView(pluginView, 0)
+ }
+ }
+ }
+ }
+ renderViewInitialized = true
+ } finally {
+ if (!renderViewInitialized) {
+ containerLayout?.removeAllViews()
+ containerLayout = null
+ }
+ }
+ return containerLayout
+ }
+
+ fun onResume(host: GodotHost) {
+ if (host != primaryHost) {
+ return
+ }
+
+ renderView!!.onActivityResumed()
+ mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
+ mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME)
+ mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME)
+ mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME)
+ if (useImmersive) {
+ val window = requireActivity().window
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or // hide nav bar
+ View.SYSTEM_UI_FLAG_FULLSCREEN or // hide status bar
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ }
+ for (plugin in pluginRegistry.allPlugins) {
+ plugin.onMainResume()
+ }
+ }
+
+ fun onPause(host: GodotHost) {
+ if (host != primaryHost) {
+ return
+ }
+
+ renderView!!.onActivityPaused()
+ mSensorManager.unregisterListener(this)
+ for (plugin in pluginRegistry.allPlugins) {
+ plugin.onMainPause()
+ }
+ }
+
+ fun onDestroy(primaryHost: GodotHost) {
+ if (this.primaryHost != primaryHost) {
+ return
+ }
+
+ for (plugin in pluginRegistry.allPlugins) {
+ plugin.onMainDestroy()
+ }
+ GodotLib.ondestroy()
+ forceQuit()
+ }
+
+ /**
+ * Activity result callback
+ */
+ fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ for (plugin in pluginRegistry.allPlugins) {
+ plugin.onMainActivityResult(requestCode, resultCode, data)
+ }
+ }
+
+ /**
+ * Permissions request callback
+ */
+ fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<String?>,
+ grantResults: IntArray
+ ) {
+ for (plugin in pluginRegistry.allPlugins) {
+ plugin.onMainRequestPermissionsResult(requestCode, permissions, grantResults)
+ }
+ for (i in permissions.indices) {
+ GodotLib.requestPermissionResult(
+ permissions[i],
+ grantResults[i] == PackageManager.PERMISSION_GRANTED
+ )
+ }
+ }
+
+ /**
+ * Invoked on the render thread when the Godot setup is complete.
+ */
+ private fun onGodotSetupCompleted() {
+ for (plugin in pluginRegistry.allPlugins) {
+ plugin.onGodotSetupCompleted()
+ }
+ primaryHost?.onGodotSetupCompleted()
+ }
+
+ /**
+ * Invoked on the render thread when the Godot main loop has started.
+ */
+ private fun onGodotMainLoopStarted() {
+ for (plugin in pluginRegistry.allPlugins) {
+ plugin.onGodotMainLoopStarted()
+ }
+ primaryHost?.onGodotMainLoopStarted()
+ }
+
+ private fun restart() {
+ primaryHost?.onGodotRestartRequested(this)
+ }
+
+ private fun registerUiChangeListener() {
+ val decorView = requireActivity().window.decorView
+ decorView.setOnSystemUiVisibilityChangeListener(uiChangeListener)
+ }
+
+ @Keep
+ private fun alert(message: String, title: String) {
+ alert(message, title, null)
+ }
+
+ private fun alert(
+ @StringRes messageResId: Int,
+ @StringRes titleResId: Int,
+ okCallback: Runnable?
+ ) {
+ val res: Resources = getActivity()?.resources ?: return
+ alert(res.getString(messageResId), res.getString(titleResId), okCallback)
+ }
+
+ private fun alert(message: String, title: String, okCallback: Runnable?) {
+ val activity: Activity = getActivity() ?: return
+ runOnUiThread(Runnable {
+ val builder = AlertDialog.Builder(activity)
+ builder.setMessage(message).setTitle(title)
+ builder.setPositiveButton(
+ "OK"
+ ) { dialog: DialogInterface, id: Int ->
+ okCallback?.run()
+ dialog.cancel()
+ }
+ val dialog = builder.create()
+ dialog.show()
+ })
+ }
+
+ /**
+ * Queue a runnable to be run on the render thread.
+ *
+ * This must be called after the render thread has started.
+ */
+ fun runOnRenderThread(action: Runnable) {
+ if (renderView != null) {
+ renderView!!.queueOnRenderThread(action)
+ }
+ }
+
+ /**
+ * Runs the specified action on the UI thread.
+ * If the current thread is the UI thread, then the action is executed immediately.
+ * If the current thread is not the UI thread, the action is posted to the event queue
+ * of the UI thread.
+ */
+ fun runOnUiThread(action: Runnable) {
+ val activity: Activity = getActivity() ?: return
+ activity.runOnUiThread(action)
+ }
+
+ /**
+ * Returns true if the call is being made on the Ui thread.
+ */
+ private fun isOnUiThread() = Looper.myLooper() == Looper.getMainLooper()
+
+ /**
+ * Returns true if `Vulkan` is used for rendering.
+ */
+ private fun usesVulkan(): Boolean {
+ val renderer = GodotLib.getGlobal("rendering/renderer/rendering_method")
+ val renderingDevice = GodotLib.getGlobal("rendering/rendering_device/driver")
+ return ("forward_plus" == renderer || "mobile" == renderer) && "vulkan" == renderingDevice
+ }
+
+ /**
+ * Returns true if the device meets the base requirements for Vulkan support, false otherwise.
+ */
+ private fun meetsVulkanRequirements(packageManager: PackageManager?): Boolean {
+ if (packageManager == null) {
+ return false
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ if (!packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL, 1)) {
+ // Optional requirements.. log as warning if missing
+ Log.w(TAG, "The vulkan hardware level does not meet the minimum requirement: 1")
+ }
+
+ // Check for api version 1.0
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x400003)
+ }
+ return false
+ }
+
+ private fun setKeepScreenOn(p_enabled: Boolean) {
+ runOnUiThread {
+ if (p_enabled) {
+ getActivity()?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ } else {
+ getActivity()?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ }
+ }
+ }
+
+ fun hasClipboard(): Boolean {
+ return mClipboard.hasPrimaryClip()
+ }
+
+ fun getClipboard(): String? {
+ val clipData = mClipboard.primaryClip ?: return ""
+ val text = clipData.getItemAt(0).text ?: return ""
+ return text.toString()
+ }
+
+ fun setClipboard(text: String?) {
+ val clip = ClipData.newPlainText("myLabel", text)
+ mClipboard.setPrimaryClip(clip)
+ }
+
+ private fun forceQuit() {
+ forceQuit(0)
+ }
+
+ @Keep
+ private fun forceQuit(instanceId: Int): Boolean {
+ if (primaryHost == null) {
+ return false
+ }
+ return if (instanceId == 0) {
+ primaryHost!!.onGodotForceQuit(this)
+ true
+ } else {
+ primaryHost!!.onGodotForceQuit(instanceId)
+ }
+ }
+
+ fun onBackPressed(host: GodotHost) {
+ if (host != primaryHost) {
+ return
+ }
+
+ var shouldQuit = true
+ for (plugin in pluginRegistry.allPlugins) {
+ if (plugin.onMainBackPressed()) {
+ shouldQuit = false
+ }
+ }
+ if (shouldQuit && renderView != null) {
+ renderView!!.queueOnRenderThread { GodotLib.back() }
+ }
+ }
+
+ private fun getRotatedValues(values: FloatArray?): FloatArray? {
+ if (values == null || values.size != 3) {
+ return values
+ }
+ val display =
+ (requireActivity().getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
+ val displayRotation = display.rotation
+ val rotatedValues = FloatArray(3)
+ when (displayRotation) {
+ 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
+ }
+ when (event.sensor.type) {
+ Sensor.TYPE_ACCELEROMETER -> {
+ val rotatedValues = getRotatedValues(event.values)
+ renderView!!.queueOnRenderThread {
+ GodotLib.accelerometer(
+ -rotatedValues!![0], -rotatedValues[1], -rotatedValues[2]
+ )
+ }
+ }
+ Sensor.TYPE_GRAVITY -> {
+ val rotatedValues = getRotatedValues(event.values)
+ renderView!!.queueOnRenderThread {
+ GodotLib.gravity(
+ -rotatedValues!![0], -rotatedValues[1], -rotatedValues[2]
+ )
+ }
+ }
+ Sensor.TYPE_MAGNETIC_FIELD -> {
+ val rotatedValues = getRotatedValues(event.values)
+ renderView!!.queueOnRenderThread {
+ GodotLib.magnetometer(
+ -rotatedValues!![0], -rotatedValues[1], -rotatedValues[2]
+ )
+ }
+ }
+ Sensor.TYPE_GYROSCOPE -> {
+ val rotatedValues = getRotatedValues(event.values)
+ renderView!!.queueOnRenderThread {
+ GodotLib.gyroscope(
+ rotatedValues!![0], rotatedValues[1], rotatedValues[2]
+ )
+ }
+ }
+ }
+ }
+
+ override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
+ // Do something here if sensor accuracy changes.
+ }
+
+ /**
+ * Used by the native code (java_godot_wrapper.h) to vibrate the device.
+ * @param durationMs
+ */
+ @SuppressLint("MissingPermission")
+ @Keep
+ private fun vibrate(durationMs: Int) {
+ if (durationMs > 0 && requestPermission("VIBRATE")) {
+ val vibratorService = getActivity()?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator? ?: return
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ vibratorService.vibrate(
+ VibrationEffect.createOneShot(
+ durationMs.toLong(),
+ VibrationEffect.DEFAULT_AMPLITUDE
+ )
+ )
+ } else {
+ // deprecated in API 26
+ vibratorService.vibrate(durationMs.toLong())
+ }
+ }
+ }
+
+ private fun getCommandLine(): MutableList<String> {
+ val original: MutableList<String> = parseCommandLine()
+ val hostCommandLine = primaryHost?.commandLine
+ if (hostCommandLine != null && hostCommandLine.isNotEmpty()) {
+ original.addAll(hostCommandLine)
+ }
+ return original
+ }
+
+ private fun parseCommandLine(): MutableList<String> {
+ val inputStream: InputStream
+ return try {
+ inputStream = requireActivity().assets.open("_cl_")
+ val len = ByteArray(4)
+ var r = inputStream.read(len)
+ if (r < 4) {
+ return mutableListOf()
+ }
+ val argc =
+ (len[3].toInt() and 0xFF) shl 24 or ((len[2].toInt() and 0xFF) shl 16) or ((len[1].toInt() and 0xFF) shl 8) or (len[0].toInt() and 0xFF)
+ val cmdline = ArrayList<String>(argc)
+ for (i in 0 until argc) {
+ r = inputStream.read(len)
+ if (r < 4) {
+ return mutableListOf()
+ }
+ val strlen =
+ (len[3].toInt() and 0xFF) shl 24 or ((len[2].toInt() and 0xFF) shl 16) or ((len[1].toInt() and 0xFF) shl 8) or (len[0].toInt() and 0xFF)
+ if (strlen > 65535) {
+ return mutableListOf()
+ }
+ val arg = ByteArray(strlen)
+ r = inputStream.read(arg)
+ if (r == strlen) {
+ cmdline[i] = String(arg, StandardCharsets.UTF_8)
+ }
+ }
+ cmdline
+ } catch (e: Exception) {
+ // The _cl_ file can be missing with no adverse effect
+ mutableListOf()
+ }
+ }
+
+ /**
+ * Used by the native code (java_godot_wrapper.h) to access the input fallback mapping.
+ * @return The input fallback mapping for the current XR mode.
+ */
+ @Keep
+ private fun getInputFallbackMapping(): String? {
+ return xrMode.inputFallbackMapping
+ }
+
+ fun requestPermission(name: String?): Boolean {
+ return requestPermission(name, getActivity())
+ }
+
+ fun requestPermissions(): Boolean {
+ return PermissionsUtil.requestManifestPermissions(getActivity())
+ }
+
+ fun getGrantedPermissions(): Array<String?>? {
+ return PermissionsUtil.getGrantedPermissions(getActivity())
+ }
+
+ @Keep
+ private fun getCACertificates(): String {
+ return GodotNetUtils.getCACertificates()
+ }
+
+ private fun obbIsCorrupted(f: String, mainPackMd5: String): Boolean {
+ return try {
+ val fis: InputStream = FileInputStream(f)
+
+ // Create MD5 Hash
+ val buffer = ByteArray(16384)
+ val complete = MessageDigest.getInstance("MD5")
+ var numRead: Int
+ do {
+ numRead = fis.read(buffer)
+ if (numRead > 0) {
+ complete.update(buffer, 0, numRead)
+ }
+ } while (numRead != -1)
+ fis.close()
+ val messageDigest = complete.digest()
+
+ // Create Hex String
+ val hexString = StringBuilder()
+ for (b in messageDigest) {
+ var s = Integer.toHexString(0xFF and b.toInt())
+ if (s.length == 1) {
+ s = "0$s"
+ }
+ hexString.append(s)
+ }
+ val md5str = hexString.toString()
+ md5str != mainPackMd5
+ } catch (e: java.lang.Exception) {
+ e.printStackTrace()
+ true
+ }
+ }
+
+ @Keep
+ private fun initInputDevices() {
+ renderView!!.initInputDevices()
+ }
+
+ @Keep
+ private fun createNewGodotInstance(args: Array<String>): Int {
+ return primaryHost?.onNewGodotInstanceRequested(args) ?: 0
+ }
+
+ @Keep
+ private fun nativeBeginBenchmarkMeasure(label: String) {
+ beginBenchmarkMeasure(label)
+ }
+
+ @Keep
+ private fun nativeEndBenchmarkMeasure(label: String) {
+ endBenchmarkMeasure(label)
+ }
+
+ @Keep
+ private fun nativeDumpBenchmark(benchmarkFile: String) {
+ dumpBenchmark(fileAccessHandler, benchmarkFile)
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt
new file mode 100644
index 0000000000..4636f753af
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt
@@ -0,0 +1,167 @@
+/**************************************************************************/
+/* GodotActivity.kt */
+/**************************************************************************/
+/* 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
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import androidx.annotation.CallSuper
+import androidx.fragment.app.FragmentActivity
+import org.godotengine.godot.utils.ProcessPhoenix
+
+/**
+ * Base abstract activity for Android apps intending to use Godot as the primary screen.
+ *
+ * Also a reference implementation for how to setup and use the [GodotFragment] fragment
+ * within an Android app.
+ */
+abstract class GodotActivity : FragmentActivity(), GodotHost {
+
+ companion object {
+ private val TAG = GodotActivity::class.java.simpleName
+
+ @JvmStatic
+ protected val EXTRA_FORCE_QUIT = "force_quit_requested"
+ @JvmStatic
+ protected val EXTRA_NEW_LAUNCH = "new_launch_requested"
+ }
+
+ /**
+ * Interaction with the [Godot] object is delegated to the [GodotFragment] class.
+ */
+ protected var godotFragment: GodotFragment? = null
+ private set
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.godot_app_layout)
+
+ handleStartIntent(intent, true)
+
+ val currentFragment = supportFragmentManager.findFragmentById(R.id.godot_fragment_container)
+ if (currentFragment is GodotFragment) {
+ Log.v(TAG, "Reusing existing Godot fragment instance.")
+ godotFragment = currentFragment
+ } else {
+ Log.v(TAG, "Creating new Godot fragment instance.")
+ godotFragment = initGodotInstance()
+ supportFragmentManager.beginTransaction().replace(R.id.godot_fragment_container, godotFragment!!).setPrimaryNavigationFragment(godotFragment).commitNowAllowingStateLoss()
+ }
+ }
+
+ override fun onDestroy() {
+ Log.v(TAG, "Destroying Godot app...")
+ super.onDestroy()
+ if (godotFragment != null) {
+ terminateGodotInstance(godotFragment!!.godot)
+ }
+ }
+
+ override fun onGodotForceQuit(instance: Godot) {
+ runOnUiThread { terminateGodotInstance(instance) }
+ }
+
+ private fun terminateGodotInstance(instance: Godot) {
+ if (godotFragment != null && instance === godotFragment!!.godot) {
+ Log.v(TAG, "Force quitting Godot instance")
+ ProcessPhoenix.forceQuit(this)
+ }
+ }
+
+ override fun onGodotRestartRequested(instance: Godot) {
+ runOnUiThread {
+ if (godotFragment != null && instance === godotFragment!!.godot) {
+ // It's very hard to properly de-initialize Godot on Android to restart the game
+ // from scratch. Therefore, we need to kill the whole app process and relaunch it.
+ //
+ // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including
+ // releasing and reloading native libs or resetting their state somehow and clearing static data).
+ Log.v(TAG, "Restarting Godot instance...")
+ ProcessPhoenix.triggerRebirth(this)
+ }
+ }
+ }
+
+ override fun onNewIntent(newIntent: Intent) {
+ super.onNewIntent(newIntent)
+ intent = newIntent
+
+ handleStartIntent(newIntent, false)
+
+ godotFragment?.onNewIntent(newIntent)
+ }
+
+ private fun handleStartIntent(intent: Intent, newLaunch: Boolean) {
+ val forceQuitRequested = intent.getBooleanExtra(EXTRA_FORCE_QUIT, false)
+ if (forceQuitRequested) {
+ Log.d(TAG, "Force quit requested, terminating..")
+ ProcessPhoenix.forceQuit(this)
+ return
+ }
+ if (!newLaunch) {
+ val newLaunchRequested = intent.getBooleanExtra(EXTRA_NEW_LAUNCH, false)
+ if (newLaunchRequested) {
+ Log.d(TAG, "New launch requested, restarting..")
+ val restartIntent = Intent(intent).putExtra(EXTRA_NEW_LAUNCH, false)
+ ProcessPhoenix.triggerRebirth(this, restartIntent)
+ return
+ }
+ }
+ }
+
+ @CallSuper
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ godotFragment?.onActivityResult(requestCode, resultCode, data)
+ }
+
+ @CallSuper
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ godotFragment?.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ }
+
+ override fun onBackPressed() {
+ godotFragment?.onBackPressed() ?: super.onBackPressed()
+ }
+
+ override fun getActivity(): Activity? {
+ return this
+ }
+
+ /**
+ * Used to initialize the Godot fragment instance in [onCreate].
+ */
+ protected open fun initGodotInstance(): GodotFragment {
+ return GodotFragment()
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
new file mode 100644
index 0000000000..9a8b10ea3e
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java
@@ -0,0 +1,429 @@
+/**************************************************************************/
+/* GodotFragment.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;
+
+import org.godotengine.godot.utils.BenchmarkUtils;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Messenger;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
+import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
+import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller;
+import com.google.android.vending.expansion.downloader.Helpers;
+import com.google.android.vending.expansion.downloader.IDownloaderClient;
+import com.google.android.vending.expansion.downloader.IDownloaderService;
+import com.google.android.vending.expansion.downloader.IStub;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Base fragment for Android apps intending to use Godot for part of the app's UI.
+ */
+public class GodotFragment extends Fragment implements IDownloaderClient, GodotHost {
+ private static final String TAG = GodotFragment.class.getSimpleName();
+
+ private IStub mDownloaderClientStub;
+ private TextView mStatusText;
+ private TextView mProgressFraction;
+ private TextView mProgressPercent;
+ private TextView mAverageSpeed;
+ private TextView mTimeRemaining;
+ private ProgressBar mPB;
+
+ private View mDashboard;
+ private View mCellMessage;
+
+ private Button mPauseButton;
+ private Button mWiFiSettingsButton;
+
+ private FrameLayout godotContainerLayout;
+ private boolean mStatePaused;
+ private int mState;
+
+ @Nullable
+ private GodotHost parentHost;
+ private Godot godot;
+
+ static private Intent mCurrentIntent;
+
+ public void onNewIntent(Intent intent) {
+ mCurrentIntent = intent;
+ }
+
+ static public Intent getCurrentIntent() {
+ return mCurrentIntent;
+ }
+
+ private void setState(int newState) {
+ if (mState != newState) {
+ mState = newState;
+ mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState));
+ }
+ }
+
+ private void setButtonPausedState(boolean paused) {
+ mStatePaused = paused;
+ int stringResourceID = paused ? R.string.text_button_resume : R.string.text_button_pause;
+ mPauseButton.setText(stringResourceID);
+ }
+
+ public interface ResultCallback {
+ void callback(int requestCode, int resultCode, Intent data);
+ }
+ public ResultCallback resultCallback;
+
+ public Godot getGodot() {
+ return godot;
+ }
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ if (getParentFragment() instanceof GodotHost) {
+ parentHost = (GodotHost)getParentFragment();
+ } else if (getActivity() instanceof GodotHost) {
+ parentHost = (GodotHost)getActivity();
+ }
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ parentHost = null;
+ }
+
+ @CallSuper
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCallback != null) {
+ resultCallback.callback(requestCode, resultCode, data);
+ resultCallback = null;
+ }
+
+ godot.onActivityResult(requestCode, resultCode, data);
+ }
+
+ @CallSuper
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ godot.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ @Override
+ public void onServiceConnected(Messenger m) {
+ IDownloaderService remoteService = DownloaderServiceMarshaller.CreateProxy(m);
+ remoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ BenchmarkUtils.beginBenchmarkMeasure("GodotFragment::onCreate");
+ super.onCreate(icicle);
+
+ final Activity activity = getActivity();
+ mCurrentIntent = activity.getIntent();
+
+ godot = new Godot(requireContext());
+ performEngineInitialization();
+ BenchmarkUtils.endBenchmarkMeasure("GodotFragment::onCreate");
+ }
+
+ private void performEngineInitialization() {
+ try {
+ godot.onCreate(this);
+
+ if (!godot.onInitNativeLayer(this)) {
+ throw new IllegalStateException("Unable to initialize engine native layer");
+ }
+
+ godotContainerLayout = godot.onInitRenderView(this);
+ if (godotContainerLayout == null) {
+ throw new IllegalStateException("Unable to initialize engine render view");
+ }
+ } catch (IllegalArgumentException ignored) {
+ final Activity activity = getActivity();
+ Intent notifierIntent = new Intent(activity, activity.getClass());
+ notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ PendingIntent pendingIntent;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ pendingIntent = PendingIntent.getActivity(activity, 0,
+ notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ } else {
+ pendingIntent = PendingIntent.getActivity(activity, 0,
+ notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ int startResult;
+ try {
+ startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(getContext(), pendingIntent, GodotDownloaderService.class);
+
+ if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
+ // This is where you do set up to display the download
+ // progress (next step in onCreateView)
+ mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this, GodotDownloaderService.class);
+ return;
+ }
+
+ // Restart engine initialization
+ performEngineInitialization();
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to start download service", e);
+ }
+ }
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle icicle) {
+ if (mDownloaderClientStub != null) {
+ View downloadingExpansionView =
+ inflater.inflate(R.layout.downloading_expansion, container, false);
+ mPB = (ProgressBar)downloadingExpansionView.findViewById(R.id.progressBar);
+ mStatusText = (TextView)downloadingExpansionView.findViewById(R.id.statusText);
+ mProgressFraction = (TextView)downloadingExpansionView.findViewById(R.id.progressAsFraction);
+ mProgressPercent = (TextView)downloadingExpansionView.findViewById(R.id.progressAsPercentage);
+ mAverageSpeed = (TextView)downloadingExpansionView.findViewById(R.id.progressAverageSpeed);
+ mTimeRemaining = (TextView)downloadingExpansionView.findViewById(R.id.progressTimeRemaining);
+ mDashboard = downloadingExpansionView.findViewById(R.id.downloaderDashboard);
+ mCellMessage = downloadingExpansionView.findViewById(R.id.approveCellular);
+ mPauseButton = (Button)downloadingExpansionView.findViewById(R.id.pauseButton);
+ mWiFiSettingsButton = (Button)downloadingExpansionView.findViewById(R.id.wifiSettingsButton);
+
+ return downloadingExpansionView;
+ }
+
+ return godotContainerLayout;
+ }
+
+ @Override
+ public void onDestroy() {
+ godot.onDestroy(this);
+ super.onDestroy();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ if (!godot.isInitialized()) {
+ if (null != mDownloaderClientStub) {
+ mDownloaderClientStub.disconnect(getActivity());
+ }
+ return;
+ }
+
+ godot.onPause(this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (!godot.isInitialized()) {
+ if (null != mDownloaderClientStub) {
+ mDownloaderClientStub.connect(getActivity());
+ }
+ return;
+ }
+
+ godot.onResume(this);
+ }
+
+ public void onBackPressed() {
+ godot.onBackPressed(this);
+ }
+
+ /**
+ * The download state should trigger changes in the UI --- it may be useful
+ * to show the state as being indeterminate at times. This sample can be
+ * considered a guideline.
+ */
+ @Override
+ public void onDownloadStateChanged(int newState) {
+ setState(newState);
+ boolean showDashboard = true;
+ boolean showCellMessage = false;
+ boolean paused;
+ boolean indeterminate;
+ switch (newState) {
+ case IDownloaderClient.STATE_IDLE:
+ // STATE_IDLE means the service is listening, so it's
+ // safe to start making remote service calls.
+ paused = false;
+ indeterminate = true;
+ break;
+ case IDownloaderClient.STATE_CONNECTING:
+ case IDownloaderClient.STATE_FETCHING_URL:
+ showDashboard = true;
+ paused = false;
+ indeterminate = true;
+ break;
+ case IDownloaderClient.STATE_DOWNLOADING:
+ paused = false;
+ showDashboard = true;
+ indeterminate = false;
+ break;
+
+ case IDownloaderClient.STATE_FAILED_CANCELED:
+ case IDownloaderClient.STATE_FAILED:
+ case IDownloaderClient.STATE_FAILED_FETCHING_URL:
+ case IDownloaderClient.STATE_FAILED_UNLICENSED:
+ paused = true;
+ showDashboard = false;
+ indeterminate = false;
+ break;
+ case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
+ case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
+ showDashboard = false;
+ paused = true;
+ indeterminate = false;
+ showCellMessage = true;
+ break;
+
+ case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
+ paused = true;
+ indeterminate = false;
+ break;
+ case IDownloaderClient.STATE_PAUSED_ROAMING:
+ case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
+ paused = true;
+ indeterminate = false;
+ break;
+ case IDownloaderClient.STATE_COMPLETED:
+ showDashboard = false;
+ paused = false;
+ indeterminate = false;
+ performEngineInitialization();
+ return;
+ default:
+ paused = true;
+ indeterminate = true;
+ showDashboard = true;
+ }
+ int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE;
+ if (mDashboard.getVisibility() != newDashboardVisibility) {
+ mDashboard.setVisibility(newDashboardVisibility);
+ }
+ int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE;
+ if (mCellMessage.getVisibility() != cellMessageVisibility) {
+ mCellMessage.setVisibility(cellMessageVisibility);
+ }
+
+ mPB.setIndeterminate(indeterminate);
+ setButtonPausedState(paused);
+ }
+
+ @Override
+ public void onDownloadProgress(DownloadProgressInfo progress) {
+ mAverageSpeed.setText(getString(R.string.kilobytes_per_second,
+ Helpers.getSpeedString(progress.mCurrentSpeed)));
+ mTimeRemaining.setText(getString(R.string.time_remaining,
+ Helpers.getTimeRemaining(progress.mTimeRemaining)));
+
+ mPB.setMax((int)(progress.mOverallTotal >> 8));
+ mPB.setProgress((int)(progress.mOverallProgress >> 8));
+ mProgressPercent.setText(String.format(Locale.ENGLISH, "%d %%", progress.mOverallProgress * 100 / progress.mOverallTotal));
+ mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress,
+ progress.mOverallTotal));
+ }
+
+ @CallSuper
+ @Override
+ public List<String> getCommandLine() {
+ return parentHost != null ? parentHost.getCommandLine() : Collections.emptyList();
+ }
+
+ @CallSuper
+ @Override
+ public void onGodotSetupCompleted() {
+ if (parentHost != null) {
+ parentHost.onGodotSetupCompleted();
+ }
+ }
+
+ @CallSuper
+ @Override
+ public void onGodotMainLoopStarted() {
+ if (parentHost != null) {
+ parentHost.onGodotMainLoopStarted();
+ }
+ }
+
+ @Override
+ public void onGodotForceQuit(Godot instance) {
+ if (parentHost != null) {
+ parentHost.onGodotForceQuit(instance);
+ }
+ }
+
+ @Override
+ public boolean onGodotForceQuit(int godotInstanceId) {
+ return parentHost != null && parentHost.onGodotForceQuit(godotInstanceId);
+ }
+
+ @Override
+ public void onGodotRestartRequested(Godot instance) {
+ if (parentHost != null) {
+ parentHost.onGodotRestartRequested(instance);
+ }
+ }
+
+ @Override
+ public int onNewGodotInstanceRequested(String[] args) {
+ if (parentHost != null) {
+ return parentHost.onNewGodotInstanceRequested(args);
+ }
+ return 0;
+ }
+}
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 b465377743..52350c12a6 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -29,10 +29,10 @@
/**************************************************************************/
package org.godotengine.godot;
+
import org.godotengine.godot.gl.GLSurfaceView;
import org.godotengine.godot.gl.GodotRenderer;
import org.godotengine.godot.input.GodotInputHandler;
-import org.godotengine.godot.utils.GLUtils;
import org.godotengine.godot.xr.XRMode;
import org.godotengine.godot.xr.ovr.OvrConfigChooser;
import org.godotengine.godot.xr.ovr.OvrContextFactory;
@@ -78,22 +78,23 @@ import java.io.InputStream;
* bit depths). Failure to do so would result in an EGL_BAD_MATCH error.
*/
public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
+ private final GodotHost host;
private final Godot godot;
private final GodotInputHandler inputHandler;
private final GodotRenderer godotRenderer;
private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>();
- public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_debug_opengl) {
- super(context);
- GLUtils.use_debug_opengl = p_use_debug_opengl;
+ public GodotGLRenderView(GodotHost host, Godot godot, XRMode xrMode, boolean useDebugOpengl) {
+ super(host.getActivity());
+ this.host = host;
this.godot = godot;
this.inputHandler = new GodotInputHandler(this);
this.godotRenderer = new GodotRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
}
- init(xrMode, false);
+ init(xrMode, false, useDebugOpengl);
}
@Override
@@ -123,7 +124,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
@Override
public void onBackPressed() {
- godot.onBackPressed();
+ godot.onBackPressed(host);
}
@Override
@@ -233,7 +234,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
return super.onResolvePointerIcon(me, pointerIndex);
}
- private void init(XRMode xrMode, boolean translucent) {
+ private void init(XRMode xrMode, boolean translucent, boolean useDebugOpengl) {
setPreserveEGLContextOnPause(true);
setFocusableInTouchMode(true);
switch (xrMode) {
@@ -262,7 +263,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
/* Setup the context factory for 2.0 rendering.
* See ContextFactory class definition below
*/
- setEGLContextFactory(new RegularContextFactory());
+ setEGLContextFactory(new RegularContextFactory(useDebugOpengl));
/* We need to choose an EGLConfig that matches the format of
* our surface exactly. This is going to be done in our
@@ -275,7 +276,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
new RegularConfigChooser(8, 8, 8, 8, 16, 0)));
break;
}
+ }
+ @Override
+ public void startRenderer() {
/* Set the renderer responsible for frame rendering */
setRenderer(godotRenderer);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
index 7700b9b628..e5333085dd 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
@@ -30,11 +30,13 @@
package org.godotengine.godot;
+import android.app.Activity;
+
import java.util.Collections;
import java.util.List;
/**
- * Denotate a component (e.g: Activity, Fragment) that hosts the {@link Godot} fragment.
+ * Denotate a component (e.g: Activity, Fragment) that hosts the {@link Godot} engine.
*/
public interface GodotHost {
/**
@@ -86,4 +88,9 @@ public interface GodotHost {
default int onNewGodotInstanceRequested(String[] args) {
return 0;
}
+
+ /**
+ * Provide access to the Activity hosting the Godot engine.
+ */
+ Activity getActivity();
}
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 00243dab2a..ebf3a6b2fb 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
@@ -39,6 +39,11 @@ public interface GodotRenderView {
void initInputDevices();
+ /**
+ * Starts the thread that will drive Godot's rendering.
+ */
+ void startRenderer();
+
void queueOnRenderThread(Runnable event);
void onActivityPaused();
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt b/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt
new file mode 100644
index 0000000000..68cd2c1358
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotService.kt
@@ -0,0 +1,54 @@
+package org.godotengine.godot
+
+import android.app.Service
+import android.content.Intent
+import android.os.Binder
+import android.os.IBinder
+import android.util.Log
+
+/**
+ * Godot service responsible for hosting the Godot engine instance.
+ */
+class GodotService : Service() {
+
+ companion object {
+ private val TAG = GodotService::class.java.simpleName
+ }
+
+ private var boundIntent: Intent? = null
+ private val godot by lazy {
+ Godot(applicationContext)
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ if (boundIntent != null) {
+ Log.d(TAG, "GodotService already bound")
+ return null
+ }
+
+ boundIntent = intent
+ return GodotHandle(godot)
+ }
+
+ override fun onRebind(intent: Intent?) {
+ super.onRebind(intent)
+ }
+
+ override fun onUnbind(intent: Intent?): Boolean {
+ return super.onUnbind(intent)
+ }
+
+ override fun onTaskRemoved(rootIntent: Intent?) {
+ super.onTaskRemoved(rootIntent)
+ }
+
+ class GodotHandle(val godot: Godot) : Binder()
+}
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 681e182adb..48708152be 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -35,7 +35,6 @@ import org.godotengine.godot.vulkan.VkRenderer;
import org.godotengine.godot.vulkan.VkSurfaceView;
import android.annotation.SuppressLint;
-import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -52,14 +51,16 @@ import androidx.annotation.Keep;
import java.io.InputStream;
public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
+ private final GodotHost host;
private final Godot godot;
private final GodotInputHandler mInputHandler;
private final VkRenderer mRenderer;
private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>();
- public GodotVulkanRenderView(Context context, Godot godot) {
- super(context);
+ public GodotVulkanRenderView(GodotHost host, Godot godot) {
+ super(host.getActivity());
+ this.host = host;
this.godot = godot;
mInputHandler = new GodotInputHandler(this);
mRenderer = new VkRenderer();
@@ -67,6 +68,10 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
}
setFocusableInTouchMode(true);
+ }
+
+ @Override
+ public void startRenderer() {
startRenderer(mRenderer);
}
@@ -97,7 +102,7 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
@Override
public void onBackPressed() {
- godot.onBackPressed();
+ godot.onBackPressed(host);
}
@Override
diff --git a/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
index edace53e7f..dce6753b7a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
@@ -33,6 +33,7 @@ package org.godotengine.godot.tts;
import org.godotengine.godot.GodotLib;
import android.app.Activity;
+import android.content.Context;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
@@ -62,7 +63,7 @@ public class GodotTTS extends UtteranceProgressListener {
final private static int EVENT_CANCEL = 2;
final private static int EVENT_BOUNDARY = 3;
- final private Activity activity;
+ private final Context context;
private TextToSpeech synth;
private LinkedList<GodotUtterance> queue;
final private Object lock = new Object();
@@ -71,8 +72,8 @@ public class GodotTTS extends UtteranceProgressListener {
private boolean speaking;
private boolean paused;
- public GodotTTS(Activity p_activity) {
- activity = p_activity;
+ public GodotTTS(Context context) {
+ this.context = context;
}
private void updateTTS() {
@@ -188,7 +189,7 @@ public class GodotTTS extends UtteranceProgressListener {
* Initialize synth and query.
*/
public void init() {
- synth = new TextToSpeech(activity, null);
+ synth = new TextToSpeech(context, null);
queue = new LinkedList<GodotUtterance>();
synth.setOnUtteranceProgressListener(this);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
index 7db02968bb..2c7b73ae4d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
@@ -44,8 +44,6 @@ public class GLUtils {
public static final boolean DEBUG = false;
- public static boolean use_debug_opengl = false;
-
private static final String[] ATTRIBUTES_NAMES = new String[] {
"EGL_BUFFER_SIZE",
"EGL_ALPHA_SIZE",
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java
index c31d56a3e1..dca190a2fc 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java
@@ -36,7 +36,8 @@ import android.net.wifi.WifiManager;
import android.util.Base64;
import android.util.Log;
-import java.io.StringWriter;
+import androidx.annotation.NonNull;
+
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
@@ -50,9 +51,9 @@ public class GodotNetUtils {
/* A single, reference counted, multicast lock, or null if permission CHANGE_WIFI_MULTICAST_STATE is missing */
private WifiManager.MulticastLock multicastLock;
- public GodotNetUtils(Activity p_activity) {
- if (PermissionsUtil.hasManifestPermission(p_activity, "android.permission.CHANGE_WIFI_MULTICAST_STATE")) {
- WifiManager wifi = (WifiManager)p_activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ public GodotNetUtils(Context context) {
+ if (PermissionsUtil.hasManifestPermission(context, "android.permission.CHANGE_WIFI_MULTICAST_STATE")) {
+ WifiManager wifi = (WifiManager)context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
multicastLock = wifi.createMulticastLock("GodotMulticastLock");
multicastLock.setReferenceCounted(true);
}
@@ -91,7 +92,7 @@ public class GodotNetUtils {
* @see https://developer.android.com/reference/java/security/KeyStore .
* @return A string of concatenated X509 certificates in PEM format.
*/
- public static String getCACertificates() {
+ public static @NonNull String getCACertificates() {
try {
KeyStore ks = KeyStore.getInstance("AndroidCAStore");
StringBuilder writer = new StringBuilder();
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
index a94188c405..8353fc8dc6 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
@@ -32,6 +32,7 @@ package org.godotengine.godot.utils;
import android.Manifest;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -52,7 +53,6 @@ import java.util.Set;
/**
* This class includes utility functions for Android permissions related operations.
*/
-
public final class PermissionsUtil {
private static final String TAG = PermissionsUtil.class.getSimpleName();
@@ -193,13 +193,13 @@ public final class PermissionsUtil {
/**
* With this function you can get the list of dangerous permissions that have been granted to the Android application.
- * @param activity the caller activity for this method.
+ * @param context the caller context for this method.
* @return granted permissions list
*/
- public static String[] getGrantedPermissions(Activity activity) {
+ public static String[] getGrantedPermissions(Context context) {
String[] manifestPermissions;
try {
- manifestPermissions = getManifestPermissions(activity);
+ manifestPermissions = getManifestPermissions(context);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return new String[0];
@@ -215,9 +215,9 @@ public final class PermissionsUtil {
grantedPermissions.add(manifestPermission);
}
} else {
- PermissionInfo permissionInfo = getPermissionInfo(activity, manifestPermission);
+ PermissionInfo permissionInfo = getPermissionInfo(context, manifestPermission);
int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
- if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, manifestPermission) == PackageManager.PERMISSION_GRANTED) {
+ if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(context, manifestPermission) == PackageManager.PERMISSION_GRANTED) {
grantedPermissions.add(manifestPermission);
}
}
@@ -232,13 +232,13 @@ public final class PermissionsUtil {
/**
* Check if the given permission is in the AndroidManifest.xml file.
- * @param activity the caller activity for this method.
+ * @param context the caller context for this method.
* @param permission the permession to look for in the manifest file.
* @return "true" if the permission is in the manifest file of the activity, "false" otherwise.
*/
- public static boolean hasManifestPermission(Activity activity, String permission) {
+ public static boolean hasManifestPermission(Context context, String permission) {
try {
- for (String p : getManifestPermissions(activity)) {
+ for (String p : getManifestPermissions(context)) {
if (permission.equals(p))
return true;
}
@@ -250,13 +250,13 @@ public final class PermissionsUtil {
/**
* Returns the permissions defined in the AndroidManifest.xml file.
- * @param activity the caller activity for this method.
+ * @param context the caller context for this method.
* @return manifest permissions list
* @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found.
*/
- private static String[] getManifestPermissions(Activity activity) throws PackageManager.NameNotFoundException {
- PackageManager packageManager = activity.getPackageManager();
- PackageInfo packageInfo = packageManager.getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS);
+ private static String[] getManifestPermissions(Context context) throws PackageManager.NameNotFoundException {
+ PackageManager packageManager = context.getPackageManager();
+ PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
if (packageInfo.requestedPermissions == null)
return new String[0];
return packageInfo.requestedPermissions;
@@ -264,13 +264,13 @@ public final class PermissionsUtil {
/**
* Returns the information of the desired permission.
- * @param activity the caller activity for this method.
+ * @param context the caller context for this method.
* @param permission the name of the permission.
* @return permission info object
* @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found.
*/
- private static PermissionInfo getPermissionInfo(Activity activity, String permission) throws PackageManager.NameNotFoundException {
- PackageManager packageManager = activity.getPackageManager();
+ private static PermissionInfo getPermissionInfo(Context context, String permission) throws PackageManager.NameNotFoundException {
+ PackageManager packageManager = context.getPackageManager();
return packageManager.getPermissionInfo(permission, 0);
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
index 1a126ff765..01ee41e30b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
@@ -51,12 +51,22 @@ public class RegularContextFactory implements GLSurfaceView.EGLContextFactory {
private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ private final boolean mUseDebugOpengl;
+
+ public RegularContextFactory() {
+ this(false);
+ }
+
+ public RegularContextFactory(boolean useDebugOpengl) {
+ this.mUseDebugOpengl = useDebugOpengl;
+ }
+
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
Log.w(TAG, "creating OpenGL ES 3.0 context :");
GLUtils.checkEglError(TAG, "Before eglCreateContext", egl);
EGLContext context;
- if (GLUtils.use_debug_opengl) {
+ if (mUseDebugOpengl) {
int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 3, _EGL_CONTEXT_FLAGS_KHR, _EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL10.EGL_NONE };
context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
} else {
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index b54491e0e1..74605e3377 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -135,7 +135,7 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv
os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion);
- return godot_java->on_video_init(env);
+ return true;
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz) {
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 862d9f0436..79ba2528ba 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -58,12 +58,10 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
}
// get some Godot method pointers...
- _on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()Z");
_restart = p_env->GetMethodID(godot_class, "restart", "()V");
_finish = p_env->GetMethodID(godot_class, "forceQuit", "(I)Z");
_set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V");
_alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
- _get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I");
_get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
_set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
_has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z");
@@ -72,20 +70,15 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;");
_get_ca_certificates = p_env->GetMethodID(godot_class, "getCACertificates", "()Ljava/lang/String;");
_init_input_devices = p_env->GetMethodID(godot_class, "initInputDevices", "()V");
- _get_surface = p_env->GetMethodID(godot_class, "getSurface", "()Landroid/view/Surface;");
- _is_activity_resumed = p_env->GetMethodID(godot_class, "isActivityResumed", "()Z");
_vibrate = p_env->GetMethodID(godot_class, "vibrate", "(I)V");
_get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;");
_on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V");
_on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");
_create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)I");
_get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
- _begin_benchmark_measure = p_env->GetMethodID(godot_class, "beginBenchmarkMeasure", "(Ljava/lang/String;)V");
- _end_benchmark_measure = p_env->GetMethodID(godot_class, "endBenchmarkMeasure", "(Ljava/lang/String;)V");
- _dump_benchmark = p_env->GetMethodID(godot_class, "dumpBenchmark", "(Ljava/lang/String;)V");
-
- // get some Activity method pointers...
- _get_class_loader = p_env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
+ _begin_benchmark_measure = p_env->GetMethodID(godot_class, "nativeBeginBenchmarkMeasure", "(Ljava/lang/String;)V");
+ _end_benchmark_measure = p_env->GetMethodID(godot_class, "nativeEndBenchmarkMeasure", "(Ljava/lang/String;)V");
+ _dump_benchmark = p_env->GetMethodID(godot_class, "nativeDumpBenchmark", "(Ljava/lang/String;)V");
}
GodotJavaWrapper::~GodotJavaWrapper() {
@@ -105,29 +98,6 @@ jobject GodotJavaWrapper::get_activity() {
return activity;
}
-jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) {
- if (godot_class) {
- if (p_env == nullptr) {
- p_env = get_jni_env();
- }
- ERR_FAIL_NULL_V(p_env, nullptr);
- jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class);
- return p_env->GetStaticObjectField(godot_class, fid);
- } else {
- return nullptr;
- }
-}
-
-jobject GodotJavaWrapper::get_class_loader() {
- if (_get_class_loader) {
- JNIEnv *env = get_jni_env();
- ERR_FAIL_NULL_V(env, nullptr);
- return env->CallObjectMethod(activity, _get_class_loader);
- } else {
- return nullptr;
- }
-}
-
GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
if (godot_view != nullptr) {
return godot_view;
@@ -143,17 +113,6 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
return godot_view;
}
-bool GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
- if (_on_video_init) {
- if (p_env == nullptr) {
- p_env = get_jni_env();
- }
- ERR_FAIL_NULL_V(p_env, false);
- return p_env->CallBooleanMethod(godot_instance, _on_video_init);
- }
- return false;
-}
-
void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) {
if (_on_godot_setup_completed) {
if (p_env == nullptr) {
@@ -212,15 +171,6 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
}
}
-int GodotJavaWrapper::get_gles_version_code() {
- JNIEnv *env = get_jni_env();
- ERR_FAIL_NULL_V(env, 0);
- if (_get_GLES_version_code) {
- return env->CallIntMethod(godot_instance, _get_GLES_version_code);
- }
- return 0;
-}
-
bool GodotJavaWrapper::has_get_clipboard() {
return _get_clipboard != nullptr;
}
@@ -333,26 +283,6 @@ void GodotJavaWrapper::init_input_devices() {
}
}
-jobject GodotJavaWrapper::get_surface() {
- if (_get_surface) {
- JNIEnv *env = get_jni_env();
- ERR_FAIL_NULL_V(env, nullptr);
- return env->CallObjectMethod(godot_instance, _get_surface);
- } else {
- return nullptr;
- }
-}
-
-bool GodotJavaWrapper::is_activity_resumed() {
- if (_is_activity_resumed) {
- JNIEnv *env = get_jni_env();
- ERR_FAIL_NULL_V(env, false);
- return env->CallBooleanMethod(godot_instance, _is_activity_resumed);
- } else {
- return false;
- }
-}
-
void GodotJavaWrapper::vibrate(int p_duration_ms) {
if (_vibrate) {
JNIEnv *env = get_jni_env();
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 1efdffd71b..ba42d5dccd 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -49,12 +49,10 @@ private:
GodotJavaViewWrapper *godot_view = nullptr;
- jmethodID _on_video_init = nullptr;
jmethodID _restart = nullptr;
jmethodID _finish = nullptr;
jmethodID _set_keep_screen_on = nullptr;
jmethodID _alert = nullptr;
- jmethodID _get_GLES_version_code = nullptr;
jmethodID _get_clipboard = nullptr;
jmethodID _set_clipboard = nullptr;
jmethodID _has_clipboard = nullptr;
@@ -63,13 +61,10 @@ private:
jmethodID _get_granted_permissions = nullptr;
jmethodID _get_ca_certificates = nullptr;
jmethodID _init_input_devices = nullptr;
- jmethodID _get_surface = nullptr;
- jmethodID _is_activity_resumed = nullptr;
jmethodID _vibrate = nullptr;
jmethodID _get_input_fallback_mapping = nullptr;
jmethodID _on_godot_setup_completed = nullptr;
jmethodID _on_godot_main_loop_started = nullptr;
- jmethodID _get_class_loader = nullptr;
jmethodID _create_new_godot_instance = nullptr;
jmethodID _get_render_view = nullptr;
jmethodID _begin_benchmark_measure = nullptr;
@@ -81,19 +76,15 @@ public:
~GodotJavaWrapper();
jobject get_activity();
- jobject get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env = nullptr);
- jobject get_class_loader();
GodotJavaViewWrapper *get_godot_view();
- bool on_video_init(JNIEnv *p_env = nullptr);
void on_godot_setup_completed(JNIEnv *p_env = nullptr);
void on_godot_main_loop_started(JNIEnv *p_env = nullptr);
void restart(JNIEnv *p_env = nullptr);
bool force_quit(JNIEnv *p_env = nullptr, int p_instance_id = 0);
void set_keep_screen_on(bool p_enabled);
void alert(const String &p_message, const String &p_title);
- int get_gles_version_code();
bool has_get_clipboard();
String get_clipboard();
bool has_set_clipboard();
@@ -105,8 +96,6 @@ public:
Vector<String> get_granted_permissions() const;
String get_ca_certificates() const;
void init_input_devices();
- jobject get_surface();
- bool is_activity_resumed();
void vibrate(int p_duration_ms);
String get_input_fallback_mapping();
int create_new_godot_instance(List<String> args);
diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
index 346cc9bf35..84bc0e1277 100644
--- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml
+++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
@@ -27,6 +27,9 @@
<member name="application/export_method_release" type="int" setter="" getter="">
Application distribution target (release export).
</member>
+ <member name="application/export_project_only" type="bool" setter="" getter="">
+ If [code]true[/code], exports iOS project files without building an XCArchive or [code].ipa[/code] file. If [code]false[/code], exports iOS project files and builds an XCArchive and [code].ipa[/code] file at the same time. When combining Godot with Fastlane or other build pipelines, you may want to set this to [code]true[/code].
+ </member>
<member name="application/icon_interpolation" type="int" setter="" getter="">
Interpolation method used to resize application icon.
</member>
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index aab46a7854..35dc058808 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -182,6 +182,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/launch_screens_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false));
+
Vector<PluginConfigIOS> found_plugins = get_plugins();
for (int i = 0; i < found_plugins.size(); i++) {
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("plugins"), found_plugins[i].name)), false));
@@ -1489,7 +1491,9 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
String dest_dir = p_path.get_base_dir() + "/";
String binary_name = p_path.get_file().get_basename();
- EditorProgress ep("export", "Exporting for iOS", 5, true);
+ bool export_project_only = p_preset->get("application/export_project_only");
+
+ EditorProgress ep("export", export_project_only ? TTR("Exporting for iOS (Project Files Only)") : TTR("Exporting for iOS"), export_project_only ? 2 : 5, true);
String team_id = p_preset->get("application/app_store_team_id");
ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project.");
@@ -1851,6 +1855,10 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
}
}
+ if (export_project_only) {
+ return OK;
+ }
+
if (ep.step("Making .xcarchive", 3)) {
return ERR_SKIP;
}
diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h
index a780669f18..7de4c0b69d 100644
--- a/platform/ios/export/export_plugin.h
+++ b/platform/ios/export/export_plugin.h
@@ -45,6 +45,7 @@
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform.h"
#include "main/splash.gen.h"
+#include "scene/resources/image_texture.h"
#include <string.h>
#include <sys/stat.h>
diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h
index cef714e86e..21bd81ed2f 100644
--- a/platform/linuxbsd/export/export_plugin.h
+++ b/platform/linuxbsd/export/export_plugin.h
@@ -34,7 +34,7 @@
#include "core/io/file_access.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform_pc.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/image_texture.h"
class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC {
GDCLASS(EditorExportPlatformLinuxBSD, EditorExportPlatformPC);
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index a7f9475342..7189a1c1c9 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -40,7 +40,7 @@
#include "core/string/print_string.h"
#include "core/string/ustring.h"
#include "main/main.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/atlas_texture.h"
#if defined(VULKAN_ENABLED)
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 2a7b2ce2a9..aef45e534f 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -315,6 +315,8 @@ public:
virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) override;
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
+ virtual Error 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) override;
+
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index a4db78b697..ac0659ee7f 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -45,7 +45,8 @@
#include "core/math/geometry_2d.h"
#include "core/os/keyboard.h"
#include "main/main.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/atlas_texture.h"
+#include "scene/resources/image_texture.h"
#if defined(GLES3_ENABLED)
#include "drivers/gles3/rasterizer_gles3.h"
@@ -1847,6 +1848,176 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect
return OK;
}
+Error DisplayServerMacOS::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) {
+ _THREAD_SAFE_METHOD_
+
+ NSString *url = [NSString stringWithUTF8String:p_current_directory.utf8().get_data()];
+ NSMutableArray *allowed_types = [[NSMutableArray alloc] init];
+ bool allow_other = false;
+ for (int i = 0; i < p_filters.size(); i++) {
+ Vector<String> tokens = p_filters[i].split(";");
+ if (tokens.size() > 0) {
+ if (tokens[0].strip_edges() == "*.*") {
+ allow_other = true;
+ } else {
+ [allowed_types addObject:[NSString stringWithUTF8String:tokens[0].replace("*.", "").strip_edges().utf8().get_data()]];
+ }
+ }
+ }
+
+ Callable callback = p_callback; // Make a copy for async completion handler.
+ switch (p_mode) {
+ case FILE_DIALOG_MODE_SAVE_FILE: {
+ NSSavePanel *panel = [NSSavePanel savePanel];
+
+ [panel setDirectoryURL:[NSURL fileURLWithPath:url]];
+ if ([allowed_types count]) {
+ [panel setAllowedFileTypes:allowed_types];
+ }
+ [panel setAllowsOtherFileTypes:allow_other];
+ [panel setExtensionHidden:YES];
+ [panel setCanSelectHiddenExtension:YES];
+ [panel setCanCreateDirectories:YES];
+ [panel setShowsHiddenFiles:p_show_hidden];
+ if (p_filename != "") {
+ NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()];
+ [panel setNameFieldStringValue:fileurl];
+ }
+
+ [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
+ completionHandler:^(NSInteger ret) {
+ if (ret == NSModalResponseOK) {
+ // Save bookmark for folder.
+ if (OS::get_singleton()->is_sandboxed()) {
+ NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
+ bool skip = false;
+ for (id bookmark in bookmarks) {
+ NSError *error = nil;
+ BOOL isStale = NO;
+ NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
+ if (!error && !isStale && ([[exurl path] compare:[[panel directoryURL] path]] == NSOrderedSame)) {
+ skip = true;
+ break;
+ }
+ }
+ if (!skip) {
+ NSError *error = nil;
+ NSData *bookmark = [[panel directoryURL] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
+ if (!error) {
+ NSArray *new_bookmarks = [bookmarks arrayByAddingObject:bookmark];
+ [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
+ }
+ }
+ }
+ // Callback.
+ Vector<String> files;
+ String url;
+ url.parse_utf8([[[panel URL] path] UTF8String]);
+ files.push_back(url);
+ if (!callback.is_null()) {
+ Variant v_status = true;
+ Variant v_files = files;
+ Variant *v_args[2] = { &v_status, &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ callback.callp((const Variant **)&v_args, 2, ret, ce);
+ }
+ } else {
+ if (!callback.is_null()) {
+ Variant v_status = false;
+ Variant v_files = Vector<String>();
+ Variant *v_args[2] = { &v_status, &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ callback.callp((const Variant **)&v_args, 2, ret, ce);
+ }
+ }
+ }];
+ } break;
+ case FILE_DIALOG_MODE_OPEN_ANY:
+ case FILE_DIALOG_MODE_OPEN_FILE:
+ case FILE_DIALOG_MODE_OPEN_FILES:
+ case FILE_DIALOG_MODE_OPEN_DIR: {
+ NSOpenPanel *panel = [NSOpenPanel openPanel];
+
+ [panel setDirectoryURL:[NSURL fileURLWithPath:url]];
+ if ([allowed_types count]) {
+ [panel setAllowedFileTypes:allowed_types];
+ }
+ [panel setAllowsOtherFileTypes:allow_other];
+ [panel setExtensionHidden:YES];
+ [panel setCanSelectHiddenExtension:YES];
+ [panel setCanCreateDirectories:YES];
+ [panel setCanChooseFiles:(p_mode != FILE_DIALOG_MODE_OPEN_DIR)];
+ [panel setCanChooseDirectories:(p_mode == FILE_DIALOG_MODE_OPEN_DIR || p_mode == FILE_DIALOG_MODE_OPEN_ANY)];
+ [panel setShowsHiddenFiles:p_show_hidden];
+ if (p_filename != "") {
+ NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()];
+ [panel setNameFieldStringValue:fileurl];
+ }
+ [panel setAllowsMultipleSelection:(p_mode == FILE_DIALOG_MODE_OPEN_FILES)];
+
+ [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
+ completionHandler:^(NSInteger ret) {
+ if (ret == NSModalResponseOK) {
+ // Save bookmark for folder.
+ NSArray *urls = [(NSOpenPanel *)panel URLs];
+ if (OS::get_singleton()->is_sandboxed()) {
+ NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
+ NSMutableArray *new_bookmarks = [bookmarks mutableCopy];
+ for (NSUInteger i = 0; i != [urls count]; ++i) {
+ bool skip = false;
+ for (id bookmark in bookmarks) {
+ NSError *error = nil;
+ BOOL isStale = NO;
+ NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
+ if (!error && !isStale && ([[exurl path] compare:[[urls objectAtIndex:i] path]] == NSOrderedSame)) {
+ skip = true;
+ break;
+ }
+ }
+ if (!skip) {
+ NSError *error = nil;
+ NSData *bookmark = [[urls objectAtIndex:i] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
+ if (!error) {
+ [new_bookmarks addObject:bookmark];
+ }
+ }
+ }
+ [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
+ }
+ // Callback.
+ Vector<String> files;
+ for (NSUInteger i = 0; i != [urls count]; ++i) {
+ String url;
+ url.parse_utf8([[[urls objectAtIndex:i] path] UTF8String]);
+ files.push_back(url);
+ }
+ if (!callback.is_null()) {
+ Variant v_status = true;
+ Variant v_files = files;
+ Variant *v_args[2] = { &v_status, &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ callback.callp((const Variant **)&v_args, 2, ret, ce);
+ }
+ } else {
+ if (!callback.is_null()) {
+ Variant v_status = false;
+ Variant v_files = Vector<String>();
+ Variant *v_args[2] = { &v_status, &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ callback.callp((const Variant **)&v_args, 2, ret, ce);
+ }
+ }
+ }];
+ } break;
+ }
+
+ return OK;
+}
+
Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
_THREAD_SAFE_METHOD_
@@ -3085,14 +3256,14 @@ bool DisplayServerMacOS::window_is_focused(WindowID p_window) const {
}
bool DisplayServerMacOS::window_can_draw(WindowID p_window) const {
- return window_get_mode(p_window) != WINDOW_MODE_MINIMIZED;
+ return (window_get_mode(p_window) != WINDOW_MODE_MINIMIZED) && [windows[p_window].window_object isOnActiveSpace];
}
bool DisplayServerMacOS::can_any_window_draw() const {
_THREAD_SAFE_METHOD_
for (const KeyValue<WindowID, WindowData> &E : windows) {
- if (window_get_mode(E.key) != WINDOW_MODE_MINIMIZED) {
+ if ((window_get_mode(E.key) != WINDOW_MODE_MINIMIZED) && [E.value.window_object isOnActiveSpace]) {
return true;
}
}
diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
index 9199701eb3..6af816989d 100644
--- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
+++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml
@@ -96,6 +96,9 @@
<member name="codesign/entitlements/app_sandbox/files_pictures" type="int" setter="" getter="">
Allows read or write access to the user's "Pictures" folder. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_assets_pictures_read-write]com.apple.security.files.pictures.read-write[/url].
</member>
+ <member name="codesign/entitlements/app_sandbox/files_user_selected" type="int" setter="" getter="">
+ Allows read or write access to the locations the user has selected using a native file dialog. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_files_user-selected_read-write]com.apple.security.files.user-selected.read-write[/url].
+ </member>
<member name="codesign/entitlements/app_sandbox/helper_executables" type="Array" setter="" getter="">
List of helper executables to embedded to the app bundle. Sandboxed app are limited to execute only these executable. See [url=https://developer.apple.com/documentation/xcode/embedding-a-helper-tool-in-a-sandboxed-app]Embedding a command-line tool in a sandboxed app[/url].
</member>
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 0dc6d0dcee..81f9707f6b 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -41,6 +41,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
+#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For svg and regex.
#ifdef MODULE_SVG_ENABLED
@@ -425,6 +426,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_user_selected", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::ARRAY, "codesign/entitlements/app_sandbox/helper_executables", PROPERTY_HINT_ARRAY_TYPE, itos(Variant::STRING) + "/" + itos(PROPERTY_HINT_GLOBAL_FILE) + ":"), Array()));
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
@@ -1359,7 +1361,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
String src_pkg_name;
- EditorProgress ep("export", "Exporting for macOS", 3, true);
+ EditorProgress ep("export", TTR("Exporting for macOS"), 3, true);
if (p_debug) {
src_pkg_name = p_preset->get("custom_template/debug");
@@ -1922,6 +1924,14 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>");
ent_f->store_line("<true/>");
}
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_user_selected") == 1) {
+ ent_f->store_line("<key>com.apple.security.files.user-selected.read-only</key>");
+ ent_f->store_line("<true/>");
+ }
+ if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_user_selected") == 2) {
+ ent_f->store_line("<key>com.apple.security.files.user-selected.read-write</key>");
+ ent_f->store_line("<true/>");
+ }
}
ent_f->store_line("</dict>");
diff --git a/platform/macos/os_macos.h b/platform/macos/os_macos.h
index ab61649d19..ae94b6296d 100644
--- a/platform/macos/os_macos.h
+++ b/platform/macos/os_macos.h
@@ -113,6 +113,10 @@ public:
virtual String get_unique_id() const override;
virtual String get_processor_name() const override;
+ virtual bool is_sandboxed() const override;
+ virtual Vector<String> get_granted_permissions() const override;
+ virtual void revoke_granted_permissions() override;
+
virtual bool _check_internal_feature_support(const String &p_feature) override;
virtual void disable_crash_handler() override;
diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm
index 69062c5920..c17ea95f4f 100644
--- a/platform/macos/os_macos.mm
+++ b/platform/macos/os_macos.mm
@@ -76,6 +76,36 @@ String OS_MacOS::get_processor_name() const {
ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string."));
}
+bool OS_MacOS::is_sandboxed() const {
+ return has_environment("APP_SANDBOX_CONTAINER_ID");
+}
+
+Vector<String> OS_MacOS::get_granted_permissions() const {
+ Vector<String> ret;
+
+ if (is_sandboxed()) {
+ NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
+ for (id bookmark in bookmarks) {
+ NSError *error = nil;
+ BOOL isStale = NO;
+ NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
+ if (!error && !isStale) {
+ String url_string;
+ url_string.parse_utf8([[url path] UTF8String]);
+ ret.push_back(url_string);
+ }
+ }
+ }
+
+ return ret;
+}
+
+void OS_MacOS::revoke_granted_permissions() {
+ if (is_sandboxed()) {
+ [[NSUserDefaults standardUserDefaults] setObject:nil forKey:@"sec_bookmarks"];
+ }
+}
+
void OS_MacOS::initialize_core() {
OS_Unix::initialize_core();
@@ -85,6 +115,18 @@ void OS_MacOS::initialize_core() {
}
void OS_MacOS::finalize() {
+ if (is_sandboxed()) {
+ NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
+ for (id bookmark in bookmarks) {
+ NSError *error = nil;
+ BOOL isStale = NO;
+ NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
+ if (!error && !isStale) {
+ [url stopAccessingSecurityScopedResource];
+ }
+ }
+ }
+
#ifdef COREMIDI_ENABLED
midi_driver.close();
#endif
@@ -733,6 +775,23 @@ void OS_MacOS::run() {
}
OS_MacOS::OS_MacOS() {
+ if (is_sandboxed()) {
+ // Load security-scoped bookmarks, request access, remove stale or invalid bookmarks.
+ NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
+ NSMutableArray *new_bookmarks = [[NSMutableArray alloc] init];
+ for (id bookmark in bookmarks) {
+ NSError *error = nil;
+ BOOL isStale = NO;
+ NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
+ if (!error && !isStale) {
+ if ([url startAccessingSecurityScopedResource]) {
+ [new_bookmarks addObject:bookmark];
+ }
+ }
+ }
+ [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
+ }
+
main_loop = nullptr;
Vector<Logger *> loggers;
diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp
index 45b484c39e..016a87e31d 100644
--- a/platform/uwp/export/export_plugin.cpp
+++ b/platform/uwp/export/export_plugin.cpp
@@ -34,6 +34,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For svg and regex.
#ifdef MODULE_SVG_ENABLED
@@ -265,7 +266,7 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p
String src_appx;
- EditorProgress ep("export", "Exporting for UWP", 7, true);
+ EditorProgress ep("export", TTR("Exporting for UWP"), 7, true);
if (p_debug) {
src_appx = p_preset->get("custom_template/debug");
diff --git a/platform/uwp/export/export_plugin.h b/platform/uwp/export/export_plugin.h
index cc86bdb280..147279e5c5 100644
--- a/platform/uwp/export/export_plugin.h
+++ b/platform/uwp/export/export_plugin.h
@@ -44,6 +44,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/export/editor_export_platform.h"
+#include "scene/resources/compressed_texture.h"
#include "thirdparty/minizip/unzip.h"
#include "thirdparty/minizip/zip.h"
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 941f2c7ef3..93b0496d74 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -35,6 +35,7 @@
#include "os_web.h"
#include "core/config/project_settings.h"
+#include "scene/resources/atlas_texture.h"
#include "servers/rendering/dummy/rasterizer_dummy.h"
#ifdef GLES3_ENABLED
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 255aefe6e7..415390f740 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
+#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For mono and svg.
#ifdef MODULE_SVG_ENABLED
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index f88cbd44b7..73e0ec6511 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -32,9 +32,10 @@
#include "os_windows.h"
+#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
#include "main/main.h"
-#include "scene/resources/texture.h"
+#include "scene/resources/atlas_texture.h"
#if defined(GLES3_ENABLED)
#include "drivers/gles3/rasterizer_gles3.h"
@@ -42,6 +43,8 @@
#include <avrt.h>
#include <dwmapi.h>
+#include <shlwapi.h>
+#include <shobjidl.h>
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
@@ -87,6 +90,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_HIDPI:
case FEATURE_ICON:
case FEATURE_NATIVE_ICON:
+ case FEATURE_NATIVE_DIALOG:
case FEATURE_SWAP_BUFFERS:
case FEATURE_KEEP_SCREEN_ON:
case FEATURE_TEXT_TO_SPEECH:
@@ -213,6 +217,129 @@ 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) {
+ _THREAD_SAFE_METHOD_
+
+ Vector<Char16String> filter_names;
+ Vector<Char16String> filter_exts;
+ for (const String &E : p_filters) {
+ Vector<String> tokens = E.split(";");
+ if (tokens.size() == 2) {
+ filter_exts.push_back(tokens[0].strip_edges().utf16());
+ filter_names.push_back(tokens[1].strip_edges().utf16());
+ } else if (tokens.size() == 1) {
+ filter_exts.push_back(tokens[0].strip_edges().utf16());
+ filter_names.push_back(tokens[0].strip_edges().utf16());
+ }
+ }
+
+ Vector<COMDLG_FILTERSPEC> filters;
+ for (int i = 0; i < filter_names.size(); i++) {
+ filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() });
+ }
+
+ HRESULT hr = S_OK;
+ IFileDialog *pfd = nullptr;
+ if (p_mode == 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);
+ }
+ if (SUCCEEDED(hr)) {
+ DWORD flags;
+ pfd->GetOptions(&flags);
+ if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) {
+ flags |= FOS_ALLOWMULTISELECT;
+ }
+ if (p_mode == FILE_DIALOG_MODE_OPEN_DIR) {
+ flags |= FOS_PICKFOLDERS;
+ }
+ if (p_show_hidden) {
+ flags |= FOS_FORCESHOWHIDDEN;
+ }
+ pfd->SetOptions(flags | FOS_FORCEFILESYSTEM);
+ pfd->SetTitle((LPCWSTR)p_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("/", "\\");
+
+ IShellItem *shellitem = nullptr;
+ hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem);
+ if (SUCCEEDED(hr)) {
+ pfd->SetDefaultFolder(shellitem);
+ pfd->SetFolder(shellitem);
+ }
+
+ pfd->SetFileName((LPCWSTR)p_filename.utf16().ptr());
+ pfd->SetFileTypes(filters.size(), filters.ptr());
+ pfd->SetFileTypeIndex(0);
+
+ hr = pfd->Show(nullptr);
+ if (SUCCEEDED(hr)) {
+ Vector<String> file_names;
+
+ if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) {
+ IShellItemArray *results;
+ hr = static_cast<IFileOpenDialog *>(pfd)->GetResults(&results);
+ if (SUCCEEDED(hr)) {
+ DWORD count = 0;
+ results->GetCount(&count);
+ for (DWORD i = 0; i < count; i++) {
+ IShellItem *result;
+ results->GetItemAt(i, &result);
+
+ PWSTR file_path = nullptr;
+ hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
+ if (SUCCEEDED(hr)) {
+ file_names.push_back(String::utf16((const char16_t *)file_path));
+ CoTaskMemFree(file_path);
+ }
+ result->Release();
+ }
+ results->Release();
+ }
+ } else {
+ IShellItem *result;
+ hr = pfd->GetResult(&result);
+ if (SUCCEEDED(hr)) {
+ PWSTR file_path = nullptr;
+ hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
+ if (SUCCEEDED(hr)) {
+ file_names.push_back(String::utf16((const char16_t *)file_path));
+ CoTaskMemFree(file_path);
+ }
+ result->Release();
+ }
+ }
+ if (!p_callback.is_null()) {
+ Variant v_status = true;
+ Variant v_files = file_names;
+ Variant *v_args[2] = { &v_status, &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ p_callback.callp((const Variant **)&v_args, 2, ret, ce);
+ }
+ } else {
+ if (!p_callback.is_null()) {
+ Variant v_status = false;
+ Variant v_files = Vector<String>();
+ Variant *v_args[2] = { &v_status, &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ p_callback.callp((const Variant **)&v_args, 2, ret, ce);
+ }
+ }
+ pfd->Release();
+
+ return OK;
+ } else {
+ return ERR_CANT_OPEN;
+ }
+}
+
void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {
_THREAD_SAFE_METHOD_
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 9d1088675b..bd47dee9ec 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -511,6 +511,8 @@ public:
virtual bool is_dark_mode() const override;
virtual Color get_accent_color() const override;
+ virtual Error 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) override;
+
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index 10fc7ef5b2..70ec57aa7a 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -142,7 +142,7 @@ void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) {
return;
}
if (shape.is_valid()) {
- shape->disconnect("changed", callable_mp(this, &CollisionShape2D::_shape_changed));
+ shape->disconnect_changed(callable_mp(this, &CollisionShape2D::_shape_changed));
}
shape = p_shape;
queue_redraw();
@@ -155,7 +155,7 @@ void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) {
}
if (shape.is_valid()) {
- shape->connect("changed", callable_mp(this, &CollisionShape2D::_shape_changed));
+ shape->connect_changed(callable_mp(this, &CollisionShape2D::_shape_changed));
}
update_configuration_warnings();
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 115104adff..1c193f991e 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -30,9 +30,12 @@
#include "cpu_particles_2d.h"
-#include "core/core_string_names.h"
#include "scene/2d/gpu_particles_2d.h"
+#include "scene/resources/atlas_texture.h"
+#include "scene/resources/curve_texture.h"
+#include "scene/resources/gradient_texture.h"
#include "scene/resources/particle_process_material.h"
+#include "scene/scene_string_names.h"
void CPUParticles2D::set_emitting(bool p_emitting) {
if (emitting == p_emitting) {
@@ -41,6 +44,7 @@ void CPUParticles2D::set_emitting(bool p_emitting) {
emitting = p_emitting;
if (emitting) {
+ active = true;
set_process_internal(true);
}
}
@@ -202,13 +206,13 @@ void CPUParticles2D::set_texture(const Ref<Texture2D> &p_texture) {
}
if (texture.is_valid()) {
- texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CPUParticles2D::_texture_changed));
+ texture->disconnect_changed(callable_mp(this, &CPUParticles2D::_texture_changed));
}
texture = p_texture;
if (texture.is_valid()) {
- texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CPUParticles2D::_texture_changed));
+ texture->connect_changed(callable_mp(this, &CPUParticles2D::_texture_changed));
}
queue_redraw();
@@ -259,7 +263,6 @@ PackedStringArray CPUParticles2D::get_configuration_warnings() const {
void CPUParticles2D::restart() {
time = 0;
- inactive_time = 0;
frame_remainder = 0;
cycle = 0;
emitting = false;
@@ -561,21 +564,15 @@ void CPUParticles2D::_update_internal() {
}
double delta = get_process_delta_time();
- if (emitting) {
- inactive_time = 0;
- } else {
- inactive_time += delta;
- if (inactive_time > lifetime * 1.2) {
- set_process_internal(false);
- _set_do_redraw(false);
+ if (!active && !emitting) {
+ set_process_internal(false);
+ _set_do_redraw(false);
- //reset variables
- time = 0;
- inactive_time = 0;
- frame_remainder = 0;
- cycle = 0;
- return;
- }
+ //reset variables
+ time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+ return;
}
_set_do_redraw(true);
@@ -650,6 +647,7 @@ void CPUParticles2D::_particles_process(double p_delta) {
double system_phase = time / lifetime;
+ bool should_be_active = false;
for (int i = 0; i < pcount; i++) {
Particle &p = parray[i];
@@ -994,6 +992,12 @@ void CPUParticles2D::_particles_process(double p_delta) {
p.transform.columns[1] *= base_scale.y;
p.transform[2] += p.velocity * local_delta;
+
+ should_be_active = true;
+ }
+ if (!Math::is_equal_approx(time, 0.0) && active && !should_be_active) {
+ active = false;
+ emit_signal(SceneStringNames::get_singleton()->finished);
}
}
@@ -1364,6 +1368,8 @@ void CPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles2D::convert_from_particles);
+ ADD_SIGNAL(MethodInfo("finished"));
+
ADD_GROUP("Emission Shape", "emission_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Sphere Surface,Rectangle,Points,Directed Points", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,suffix:px"), "set_emission_sphere_radius", "get_emission_sphere_radius");
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index d4ff999459..3f858c3277 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -78,6 +78,7 @@ public:
private:
bool emitting = false;
+ bool active = false;
struct Particle {
Transform2D transform;
@@ -99,7 +100,6 @@ private:
};
double time = 0.0;
- double inactive_time = 0.0;
double frame_remainder = 0.0;
int cycle = 0;
bool do_redraw = false;
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index 735159c660..8c5782dc41 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -30,21 +30,39 @@
#include "gpu_particles_2d.h"
-#include "core/core_string_names.h"
+#include "scene/resources/atlas_texture.h"
#include "scene/resources/particle_process_material.h"
+#include "scene/scene_string_names.h"
#ifdef TOOLS_ENABLED
#include "core/config/engine.h"
#endif
void GPUParticles2D::set_emitting(bool p_emitting) {
- RS::get_singleton()->particles_set_emitting(particles, p_emitting);
+ // Do not return even if `p_emitting == emitting` because `emitting` is just an approximation.
if (p_emitting && one_shot) {
+ if (!active && !emitting) {
+ // Last cycle ended.
+ active = true;
+ time = 0;
+ signal_cancled = false;
+ emission_time = lifetime;
+ active_time = lifetime * (2 - explosiveness_ratio);
+ } else {
+ signal_cancled = true;
+ }
set_process_internal(true);
} else if (!p_emitting) {
- set_process_internal(false);
+ if (one_shot) {
+ set_process_internal(true);
+ } else {
+ set_process_internal(false);
+ }
}
+
+ emitting = p_emitting;
+ RS::get_singleton()->particles_set_emitting(particles, p_emitting);
}
void GPUParticles2D::set_amount(int p_amount) {
@@ -211,7 +229,7 @@ void GPUParticles2D::set_speed_scale(double p_scale) {
}
bool GPUParticles2D::is_emitting() const {
- return RS::get_singleton()->particles_get_emitting(particles);
+ return emitting;
}
int GPUParticles2D::get_amount() const {
@@ -338,13 +356,13 @@ Rect2 GPUParticles2D::capture_rect() const {
void GPUParticles2D::set_texture(const Ref<Texture2D> &p_texture) {
if (texture.is_valid()) {
- texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GPUParticles2D::_texture_changed));
+ texture->disconnect_changed(callable_mp(this, &GPUParticles2D::_texture_changed));
}
texture = p_texture;
if (texture.is_valid()) {
- texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GPUParticles2D::_texture_changed));
+ texture->connect_changed(callable_mp(this, &GPUParticles2D::_texture_changed));
}
_update_collision_size();
queue_redraw();
@@ -405,6 +423,16 @@ NodePath GPUParticles2D::get_sub_emitter() const {
void GPUParticles2D::restart() {
RS::get_singleton()->particles_restart(particles);
RS::get_singleton()->particles_set_emitting(particles, true);
+
+ emitting = true;
+ active = true;
+ signal_cancled = false;
+ time = 0;
+ emission_time = lifetime;
+ active_time = lifetime * (2 - explosiveness_ratio);
+ if (one_shot) {
+ set_process_internal(true);
+ }
}
void GPUParticles2D::_notification(int p_what) {
@@ -570,9 +598,23 @@ void GPUParticles2D::_notification(int p_what) {
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
- if (one_shot && !is_emitting()) {
- notify_property_list_changed();
- set_process_internal(false);
+ if (one_shot) {
+ time += get_process_delta_time();
+ if (time > emission_time) {
+ emitting = false;
+ if (!active) {
+ set_process_internal(false);
+ }
+ }
+ if (time > active_time) {
+ if (active && !signal_cancled) {
+ emit_signal(SceneStringNames::get_singleton()->finished);
+ }
+ active = false;
+ if (!emitting) {
+ set_process_internal(false);
+ }
+ }
}
} break;
}
@@ -638,6 +680,8 @@ void GPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_trail_section_subdivisions", "subdivisions"), &GPUParticles2D::set_trail_section_subdivisions);
ClassDB::bind_method(D_METHOD("get_trail_section_subdivisions"), &GPUParticles2D::get_trail_section_subdivisions);
+ ADD_SIGNAL(MethodInfo("finished"));
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false.
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h
index e518ffec6f..3131698e5c 100644
--- a/scene/2d/gpu_particles_2d.h
+++ b/scene/2d/gpu_particles_2d.h
@@ -47,6 +47,9 @@ public:
private:
RID particles;
+ bool emitting = false;
+ bool active = false;
+ bool signal_cancled = false;
bool one_shot = false;
int amount = 0;
double lifetime = 0.0;
@@ -78,6 +81,10 @@ private:
int trail_sections = 8;
int trail_section_subdivisions = 4;
+ double time = 0.0;
+ double emission_time = 0.0;
+ double active_time = 0.0;
+
RID mesh;
void _attach_sub_emitter();
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index 4c3161d049..61f5d5cd46 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -215,7 +215,7 @@ bool LightOccluder2D::_edit_is_selected_on_click(const Point2 &p_point, double p
void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon) {
#ifdef DEBUG_ENABLED
if (occluder_polygon.is_valid()) {
- occluder_polygon->disconnect("changed", callable_mp(this, &LightOccluder2D::_poly_changed));
+ occluder_polygon->disconnect_changed(callable_mp(this, &LightOccluder2D::_poly_changed));
}
#endif
occluder_polygon = p_polygon;
@@ -228,7 +228,7 @@ void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polyg
#ifdef DEBUG_ENABLED
if (occluder_polygon.is_valid()) {
- occluder_polygon->connect("changed", callable_mp(this, &LightOccluder2D::_poly_changed));
+ occluder_polygon->connect_changed(callable_mp(this, &LightOccluder2D::_poly_changed));
}
queue_redraw();
#endif
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 58dad40403..3a9473d76c 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -30,7 +30,6 @@
#include "line_2d.h"
-#include "core/core_string_names.h"
#include "core/math/geometry_2d.h"
#include "line_builder.h"
@@ -89,14 +88,14 @@ float Line2D::get_width() const {
void Line2D::set_curve(const Ref<Curve> &p_curve) {
// Cleanup previous connection if any
if (_curve.is_valid()) {
- _curve->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_curve_changed));
+ _curve->disconnect_changed(callable_mp(this, &Line2D::_curve_changed));
}
_curve = p_curve;
// Connect to the curve so the line will update when it is changed
if (_curve.is_valid()) {
- _curve->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_curve_changed));
+ _curve->connect_changed(callable_mp(this, &Line2D::_curve_changed));
}
queue_redraw();
@@ -159,14 +158,14 @@ Color Line2D::get_default_color() const {
void Line2D::set_gradient(const Ref<Gradient> &p_gradient) {
// Cleanup previous connection if any
if (_gradient.is_valid()) {
- _gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_gradient_changed));
+ _gradient->disconnect_changed(callable_mp(this, &Line2D::_gradient_changed));
}
_gradient = p_gradient;
// Connect to the gradient so the line will update when the Gradient is changed
if (_gradient.is_valid()) {
- _gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Line2D::_gradient_changed));
+ _gradient->connect_changed(callable_mp(this, &Line2D::_gradient_changed));
}
queue_redraw();
diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp
index f347eb6520..9631b2cc4e 100644
--- a/scene/2d/multimesh_instance_2d.cpp
+++ b/scene/2d/multimesh_instance_2d.cpp
@@ -30,7 +30,6 @@
#include "multimesh_instance_2d.h"
-#include "core/core_string_names.h"
#include "scene/scene_string_names.h"
void MultiMeshInstance2D::_notification(int p_what) {
@@ -59,13 +58,13 @@ void MultiMeshInstance2D::_bind_methods() {
void MultiMeshInstance2D::set_multimesh(const Ref<MultiMesh> &p_multimesh) {
// Cleanup previous connection if any.
if (multimesh.is_valid()) {
- multimesh->disconnect(CoreStringNames::get_singleton()->changed, callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
+ multimesh->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
}
multimesh = p_multimesh;
// Connect to the multimesh so the AABB can update when instance transforms are changed.
if (multimesh.is_valid()) {
- multimesh->connect(CoreStringNames::get_singleton()->changed, callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
+ multimesh->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
}
queue_redraw();
}
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index 8e96f8265c..d993b8a400 100644
--- a/scene/2d/navigation_obstacle_2d.cpp
+++ b/scene/2d/navigation_obstacle_2d.cpp
@@ -79,7 +79,7 @@ void NavigationObstacle2D::_notification(int p_what) {
previous_transform = get_global_transform();
// need to trigger map controlled agent assignment somehow for the fake_agent since obstacles use no callback like regular agents
NavigationServer2D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
- _update_position(get_global_transform().get_origin());
+ _update_position(get_global_position());
set_physics_process_internal(true);
} break;
@@ -112,7 +112,7 @@ void NavigationObstacle2D::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (is_inside_tree()) {
- _update_position(get_global_transform().get_origin());
+ _update_position(get_global_position());
if (velocity_submitted) {
velocity_submitted = false;
@@ -164,9 +164,9 @@ NavigationObstacle2D::~NavigationObstacle2D() {
void NavigationObstacle2D::set_vertices(const Vector<Vector2> &p_vertices) {
vertices = p_vertices;
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, vertices);
- if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
- queue_redraw();
- }
+#ifdef DEBUG_ENABLED
+ queue_redraw();
+#endif // DEBUG_ENABLED
}
void NavigationObstacle2D::set_navigation_map(RID p_navigation_map) {
@@ -195,9 +195,9 @@ void NavigationObstacle2D::set_radius(real_t p_radius) {
radius = p_radius;
NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, radius);
- if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
- queue_redraw();
- }
+#ifdef DEBUG_ENABLED
+ queue_redraw();
+#endif // DEBUG_ENABLED
}
void NavigationObstacle2D::set_avoidance_layers(uint32_t p_layers) {
@@ -237,6 +237,9 @@ void NavigationObstacle2D::set_avoidance_enabled(bool p_enabled) {
avoidance_enabled = p_enabled;
NavigationServer2D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
+#ifdef DEBUG_ENABLED
+ queue_redraw();
+#endif // DEBUG_ENABLED
}
bool NavigationObstacle2D::get_avoidance_enabled() const {
@@ -255,13 +258,16 @@ void NavigationObstacle2D::_update_map(RID p_map) {
void NavigationObstacle2D::_update_position(const Vector2 p_position) {
NavigationServer2D::get_singleton()->obstacle_set_position(obstacle, p_position);
+#ifdef DEBUG_ENABLED
+ queue_redraw();
+#endif // DEBUG_ENABLED
}
#ifdef DEBUG_ENABLED
void NavigationObstacle2D::_update_fake_agent_radius_debug() {
if (radius > 0.0 && NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius()) {
Color debug_radius_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_color();
- draw_circle(get_global_transform().get_origin(), radius, debug_radius_color);
+ RS::get_singleton()->canvas_item_add_circle(get_canvas_item(), Vector2(), radius, debug_radius_color);
}
}
#endif // DEBUG_ENABLED
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 529545de97..fa03713592 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -30,7 +30,6 @@
#include "navigation_region_2d.h"
-#include "core/core_string_names.h"
#include "core/math/geometry_2d.h"
#include "scene/2d/navigation_obstacle_2d.h"
#include "scene/resources/world_2d.h"
@@ -193,14 +192,14 @@ void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_
}
if (navigation_polygon.is_valid()) {
- navigation_polygon->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed));
+ navigation_polygon->disconnect_changed(callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed));
}
navigation_polygon = p_navigation_polygon;
NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, p_navigation_polygon);
if (navigation_polygon.is_valid()) {
- navigation_polygon->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed));
+ navigation_polygon->connect_changed(callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed));
}
_navigation_polygon_changed();
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index 3e6a484e72..ee0ccc42ff 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -182,13 +182,13 @@ void Path2D::_curve_changed() {
void Path2D::set_curve(const Ref<Curve2D> &p_curve) {
if (curve.is_valid()) {
- curve->disconnect("changed", callable_mp(this, &Path2D::_curve_changed));
+ curve->disconnect_changed(callable_mp(this, &Path2D::_curve_changed));
}
curve = p_curve;
if (curve.is_valid()) {
- curve->connect("changed", callable_mp(this, &Path2D::_curve_changed));
+ curve->connect_changed(callable_mp(this, &Path2D::_curve_changed));
}
_curve_changed();
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index b3acc1849b..b9bde47507 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -30,7 +30,6 @@
#include "physics_body_2d.h"
-#include "core/core_string_names.h"
#include "scene/scene_string_names.h"
void PhysicsBody2D::_bind_methods() {
@@ -195,15 +194,13 @@ real_t StaticBody2D::get_constant_angular_velocity() const {
void StaticBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
if (physics_material_override.is_valid()) {
- if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody2D::_reload_physics_characteristics))) {
- physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody2D::_reload_physics_characteristics));
- }
+ physics_material_override->disconnect_changed(callable_mp(this, &StaticBody2D::_reload_physics_characteristics));
}
physics_material_override = p_physics_material_override;
if (physics_material_override.is_valid()) {
- physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody2D::_reload_physics_characteristics));
+ physics_material_override->connect_changed(callable_mp(this, &StaticBody2D::_reload_physics_characteristics));
}
_reload_physics_characteristics();
}
@@ -651,15 +648,13 @@ const Vector2 &RigidBody2D::get_center_of_mass() const {
void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
if (physics_material_override.is_valid()) {
- if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics))) {
- physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics));
- }
+ physics_material_override->disconnect_changed(callable_mp(this, &RigidBody2D::_reload_physics_characteristics));
}
physics_material_override = p_physics_material_override;
if (physics_material_override.is_valid()) {
- physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody2D::_reload_physics_characteristics));
+ physics_material_override->connect_changed(callable_mp(this, &RigidBody2D::_reload_physics_characteristics));
}
_reload_physics_characteristics();
}
diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp
index f1a9119458..90d80d7549 100644
--- a/scene/2d/shape_cast_2d.cpp
+++ b/scene/2d/shape_cast_2d.cpp
@@ -31,7 +31,6 @@
#include "shape_cast_2d.h"
#include "core/config/engine.h"
-#include "core/core_string_names.h"
#include "scene/2d/collision_object_2d.h"
#include "scene/2d/physics_body_2d.h"
#include "scene/resources/circle_shape_2d.h"
@@ -155,11 +154,11 @@ void ShapeCast2D::set_shape(const Ref<Shape2D> &p_shape) {
return;
}
if (shape.is_valid()) {
- shape->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast2D::_shape_changed));
+ shape->disconnect_changed(callable_mp(this, &ShapeCast2D::_shape_changed));
}
shape = p_shape;
if (shape.is_valid()) {
- shape->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast2D::_shape_changed));
+ shape->connect_changed(callable_mp(this, &ShapeCast2D::_shape_changed));
shape_rid = shape->get_rid();
}
diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp
index 6e314160fd..7e6b43559c 100644
--- a/scene/2d/sprite_2d.cpp
+++ b/scene/2d/sprite_2d.cpp
@@ -30,7 +30,6 @@
#include "sprite_2d.h"
-#include "core/core_string_names.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
@@ -137,13 +136,13 @@ void Sprite2D::set_texture(const Ref<Texture2D> &p_texture) {
}
if (texture.is_valid()) {
- texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Sprite2D::_texture_changed));
+ texture->disconnect_changed(callable_mp(this, &Sprite2D::_texture_changed));
}
texture = p_texture;
if (texture.is_valid()) {
- texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Sprite2D::_texture_changed));
+ texture->connect_changed(callable_mp(this, &Sprite2D::_texture_changed));
}
queue_redraw();
diff --git a/scene/2d/tile_map.compat.inc b/scene/2d/tile_map.compat.inc
new file mode 100644
index 0000000000..49e2bf6f0b
--- /dev/null
+++ b/scene/2d/tile_map.compat.inc
@@ -0,0 +1,45 @@
+/**************************************************************************/
+/* object.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
+
+#include "core/object/object.h"
+
+#include "core/object/class_db.h"
+
+Rect2i TileMap::_get_used_rect_bind_compat_78328() {
+ return get_used_rect();
+}
+
+void TileMap::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_used_rect"), &TileMap::_get_used_rect_bind_compat_78328);
+}
+
+#endif
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index c5ce45091e..99a79e023d 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -29,7 +29,9 @@
/**************************************************************************/
#include "tile_map.h"
+#include "tile_map.compat.inc"
+#include "core/core_string_names.h"
#include "core/io/marshalls.h"
#include "scene/resources/world_2d.h"
#include "servers/navigation_server_2d.h"
@@ -38,793 +40,17 @@
#include "servers/navigation_server_3d.h"
#endif // DEBUG_ENABLED
-HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const {
- HashMap<Vector2i, TileSet::CellNeighbor> output;
-
- ERR_FAIL_COND_V(is_center_bit(), output);
-
- Ref<TileSet> ts = tile_map->get_tileset();
- ERR_FAIL_COND_V(!ts.is_valid(), output);
-
- TileSet::TileShape shape = ts->get_tile_shape();
- if (shape == TileSet::TILE_SHAPE_SQUARE) {
- switch (bit) {
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
- break;
- default:
- ERR_FAIL_V(output);
- }
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- switch (bit) {
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- break;
- default:
- ERR_FAIL_V(output);
- }
- } else {
- // Half offset shapes.
- TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis();
- if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- switch (bit) {
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
- break;
- case 4:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
- break;
- case 5:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- break;
- default:
- ERR_FAIL_V(output);
- }
- } else {
- switch (bit) {
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- break;
- case 4:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
- break;
- case 5:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- break;
- default:
- ERR_FAIL_V(output);
- }
- }
- }
- return output;
-}
-
-TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain) {
- tile_map = p_tile_map;
-
- Ref<TileSet> ts = tile_map->get_tileset();
- ERR_FAIL_COND(!ts.is_valid());
-
- bit = 0;
- base_cell_coords = p_position;
- terrain = p_terrain;
-}
-
-TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
- // The way we build the constraint make it easy to detect conflicting constraints.
- tile_map = p_tile_map;
-
- Ref<TileSet> ts = tile_map->get_tileset();
- ERR_FAIL_COND(!ts.is_valid());
-
- TileSet::TileShape shape = ts->get_tile_shape();
- if (shape == TileSet::TILE_SHAPE_SQUARE) {
- switch (p_bit) {
- case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
- bit = 1;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
- bit = 2;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
- bit = 3;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_SIDE:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
- break;
- default:
- ERR_FAIL();
- break;
- }
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- switch (p_bit) {
- case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
- bit = 1;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
- bit = 2;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
- bit = 3;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- default:
- ERR_FAIL();
- break;
- }
- } else {
- // Half-offset shapes
- TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis();
- if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- switch (p_bit) {
- case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
- bit = 1;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
- bit = 2;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
- bit = 3;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
- bit = 4;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
- bit = 5;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
- bit = 4;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
- bit = 5;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
- bit = 4;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- default:
- ERR_FAIL();
- break;
- }
- } else {
- switch (p_bit) {
- case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
- bit = 1;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
- bit = 2;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
- bit = 3;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
- bit = 4;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
- bit = 5;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_SIDE:
- bit = 4;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
- bit = 5;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- default:
- ERR_FAIL();
- break;
- }
- }
- }
- terrain = p_terrain;
-}
+Vector2i TileMapLayer::_coords_to_quadrant_coords(const Vector2i &p_coords) const {
+ int quad_size = get_effective_quadrant_size();
-Vector2i TileMap::transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) {
- // Transform to stacked layout.
- Vector2i output = p_coords;
- if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
- SWAP(output.x, output.y);
- }
- switch (p_from_layout) {
- case TileSet::TILE_LAYOUT_STACKED:
- break;
- case TileSet::TILE_LAYOUT_STACKED_OFFSET:
- if (output.y % 2) {
- output.x -= 1;
- }
- break;
- case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
- case TileSet::TILE_LAYOUT_STAIRS_DOWN:
- if ((p_from_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
- if (output.y < 0 && bool(output.y % 2)) {
- output = Vector2i(output.x + output.y / 2 - 1, output.y);
- } else {
- output = Vector2i(output.x + output.y / 2, output.y);
- }
- } else {
- if (output.x < 0 && bool(output.x % 2)) {
- output = Vector2i(output.x / 2 - 1, output.x + output.y * 2);
- } else {
- output = Vector2i(output.x / 2, output.x + output.y * 2);
- }
- }
- break;
- case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
- case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
- if ((p_from_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
- if ((output.x + output.y) < 0 && (output.x - output.y) % 2) {
- output = Vector2i((output.x + output.y) / 2 - 1, output.y - output.x);
- } else {
- output = Vector2i((output.x + output.y) / 2, -output.x + output.y);
- }
- } else {
- if ((output.x - output.y) < 0 && (output.x + output.y) % 2) {
- output = Vector2i((output.x - output.y) / 2 - 1, output.x + output.y);
- } else {
- output = Vector2i((output.x - output.y) / 2, output.x + output.y);
- }
- }
- break;
- }
-
- switch (p_to_layout) {
- case TileSet::TILE_LAYOUT_STACKED:
- break;
- case TileSet::TILE_LAYOUT_STACKED_OFFSET:
- if (output.y % 2) {
- output.x += 1;
- }
- break;
- case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
- case TileSet::TILE_LAYOUT_STAIRS_DOWN:
- if ((p_to_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
- if (output.y < 0 && (output.y % 2)) {
- output = Vector2i(output.x - output.y / 2 + 1, output.y);
- } else {
- output = Vector2i(output.x - output.y / 2, output.y);
- }
- } else {
- if (output.y % 2) {
- if (output.y < 0) {
- output = Vector2i(2 * output.x + 1, -output.x + output.y / 2 - 1);
- } else {
- output = Vector2i(2 * output.x + 1, -output.x + output.y / 2);
- }
- } else {
- output = Vector2i(2 * output.x, -output.x + output.y / 2);
- }
- }
- break;
- case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
- case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
- if ((p_to_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
- if (output.y % 2) {
- if (output.y > 0) {
- output = Vector2i(output.x - output.y / 2, output.x + output.y / 2 + 1);
- } else {
- output = Vector2i(output.x - output.y / 2 + 1, output.x + output.y / 2);
- }
- } else {
- output = Vector2i(output.x - output.y / 2, output.x + output.y / 2);
- }
- } else {
- if (output.y % 2) {
- if (output.y < 0) {
- output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2 - 1);
- } else {
- output = Vector2i(output.x + output.y / 2 + 1, -output.x + output.y / 2);
- }
- } else {
- output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2);
- }
- }
- break;
- }
-
- if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
- SWAP(output.x, output.y);
- }
-
- return output;
-}
-
-int TileMap::get_effective_quadrant_size(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), 1);
-
- // When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant
- if (is_y_sort_enabled() && layers[p_layer].y_sort_enabled) {
- return 1;
- } else {
- return quadrant_size;
- }
-}
-
-void TileMap::set_selected_layer(int p_layer_id) {
- ERR_FAIL_COND(p_layer_id < -1 || p_layer_id >= (int)layers.size());
- selected_layer = p_layer_id;
- emit_signal(SNAME("changed"));
-
- // Update the layers modulation.
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- _rendering_update_layer(layer);
- }
-}
-
-int TileMap::get_selected_layer() const {
- return selected_layer;
-}
-
-void TileMap::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- _clear_internals();
- _recreate_internals();
- } break;
-
- case NOTIFICATION_EXIT_TREE: {
- _clear_internals();
- } break;
- }
-
- // Transfers the notification to tileset plugins.
- if (tile_set.is_valid()) {
- _rendering_notification(p_what);
- _physics_notification(p_what);
- _navigation_notification(p_what);
- }
-}
-
-Ref<TileSet> TileMap::get_tileset() const {
- return tile_set;
-}
-
-void TileMap::set_tileset(const Ref<TileSet> &p_tileset) {
- if (p_tileset == tile_set) {
- return;
- }
-
- // Set the tileset, registering to its changes.
- if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed));
- }
-
- if (!p_tileset.is_valid()) {
- _clear_internals();
- }
-
- tile_set = p_tileset;
-
- if (tile_set.is_valid()) {
- tile_set->connect("changed", callable_mp(this, &TileMap::_tile_set_changed));
- _clear_internals();
- _recreate_internals();
- }
-
- emit_signal(SNAME("changed"));
-}
-
-void TileMap::set_quadrant_size(int p_size) {
- ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
-
- quadrant_size = p_size;
- _clear_internals();
- _recreate_internals();
- emit_signal(SNAME("changed"));
-}
-
-int TileMap::get_quadrant_size() const {
- return quadrant_size;
-}
-
-int TileMap::get_layers_count() const {
- return layers.size();
-}
-
-void TileMap::add_layer(int p_to_pos) {
- if (p_to_pos < 0) {
- p_to_pos = layers.size() + p_to_pos + 1;
- }
-
- ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
-
- // Must clear before adding the layer.
- _clear_internals();
-
- layers.insert(p_to_pos, TileMapLayer());
- _recreate_internals();
- notify_property_list_changed();
-
- emit_signal(SNAME("changed"));
-
- update_configuration_warnings();
-}
-
-void TileMap::move_layer(int p_layer, int p_to_pos) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
- ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
-
- // Clear before shuffling layers.
- _clear_internals();
-
- TileMapLayer tl = layers[p_layer];
- layers.insert(p_to_pos, tl);
- layers.remove_at(p_to_pos < p_layer ? p_layer + 1 : p_layer);
- _recreate_internals();
- notify_property_list_changed();
-
- if (selected_layer == p_layer) {
- selected_layer = p_to_pos < p_layer ? p_to_pos - 1 : p_to_pos;
- }
-
- emit_signal(SNAME("changed"));
-
- update_configuration_warnings();
-}
-
-void TileMap::remove_layer(int p_layer) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
- // Clear before removing the layer.
- _clear_internals();
-
- layers.remove_at(p_layer);
- _recreate_internals();
- notify_property_list_changed();
-
- if (selected_layer >= p_layer) {
- selected_layer -= 1;
- }
-
- emit_signal(SNAME("changed"));
-
- update_configuration_warnings();
-}
-
-void TileMap::set_layer_name(int p_layer, String p_name) {
- if (p_layer < 0) {
- p_layer = layers.size() + p_layer;
- }
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
- if (layers[p_layer].name == p_name) {
- return;
- }
- layers[p_layer].name = p_name;
- emit_signal(SNAME("changed"));
-}
-
-String TileMap::get_layer_name(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), String());
- return layers[p_layer].name;
-}
-
-void TileMap::set_layer_enabled(int p_layer, bool p_enabled) {
- if (p_layer < 0) {
- p_layer = layers.size() + p_layer;
- }
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
- if (layers[p_layer].enabled == p_enabled) {
- return;
- }
- layers[p_layer].enabled = p_enabled;
- _clear_layer_internals(p_layer);
- _recreate_layer_internals(p_layer);
- emit_signal(SNAME("changed"));
-
- update_configuration_warnings();
-}
-
-bool TileMap::is_layer_enabled(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false);
- return layers[p_layer].enabled;
-}
-
-void TileMap::set_layer_modulate(int p_layer, Color p_modulate) {
- if (p_layer < 0) {
- p_layer = layers.size() + p_layer;
- }
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
- if (layers[p_layer].modulate == p_modulate) {
- return;
- }
- layers[p_layer].modulate = p_modulate;
- _rendering_update_layer(p_layer);
- emit_signal(SNAME("changed"));
-}
-
-Color TileMap::get_layer_modulate(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Color());
- return layers[p_layer].modulate;
-}
-
-void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) {
- if (p_layer < 0) {
- p_layer = layers.size() + p_layer;
- }
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
- if (layers[p_layer].y_sort_enabled == p_y_sort_enabled) {
- return;
- }
- layers[p_layer].y_sort_enabled = p_y_sort_enabled;
- _clear_layer_internals(p_layer);
- _recreate_layer_internals(p_layer);
- emit_signal(SNAME("changed"));
-
- update_configuration_warnings();
-}
-
-bool TileMap::is_layer_y_sort_enabled(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false);
- return layers[p_layer].y_sort_enabled;
-}
-
-void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) {
- if (p_layer < 0) {
- p_layer = layers.size() + p_layer;
- }
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
- if (layers[p_layer].y_sort_origin == p_y_sort_origin) {
- return;
- }
- layers[p_layer].y_sort_origin = p_y_sort_origin;
- _clear_layer_internals(p_layer);
- _recreate_layer_internals(p_layer);
- emit_signal(SNAME("changed"));
-}
-
-int TileMap::get_layer_y_sort_origin(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false);
- return layers[p_layer].y_sort_origin;
-}
-
-void TileMap::set_layer_z_index(int p_layer, int p_z_index) {
- if (p_layer < 0) {
- p_layer = layers.size() + p_layer;
- }
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
- if (layers[p_layer].z_index == p_z_index) {
- return;
- }
- layers[p_layer].z_index = p_z_index;
- _rendering_update_layer(p_layer);
- emit_signal(SNAME("changed"));
-
- update_configuration_warnings();
-}
-
-int TileMap::get_layer_z_index(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false);
- return layers[p_layer].z_index;
-}
-
-void TileMap::set_collision_animatable(bool p_enabled) {
- if (collision_animatable == p_enabled) {
- return;
- }
- collision_animatable = p_enabled;
- _clear_internals();
- set_notify_local_transform(p_enabled);
- set_physics_process_internal(p_enabled);
- _recreate_internals();
- emit_signal(SNAME("changed"));
-}
-
-bool TileMap::is_collision_animatable() const {
- return collision_animatable;
-}
-
-void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) {
- if (collision_visibility_mode == p_show_collision) {
- return;
- }
- collision_visibility_mode = p_show_collision;
- _clear_internals();
- _recreate_internals();
- emit_signal(SNAME("changed"));
-}
-
-TileMap::VisibilityMode TileMap::get_collision_visibility_mode() {
- return collision_visibility_mode;
-}
-
-void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navigation) {
- if (navigation_visibility_mode == p_show_navigation) {
- return;
- }
- navigation_visibility_mode = p_show_navigation;
- _clear_internals();
- _recreate_internals();
- emit_signal(SNAME("changed"));
-}
-
-TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() {
- return navigation_visibility_mode;
-}
-
-void TileMap::set_navigation_map(int p_layer, RID p_map) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
- ERR_FAIL_COND_MSG(!is_inside_tree(), "A TileMap navigation map can only be changed while inside the SceneTree.");
- layers[p_layer].navigation_map = p_map;
- layers[p_layer].uses_world_navigation_map = p_map == get_world_2d()->get_navigation_map();
-}
-
-RID TileMap::get_navigation_map(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), RID());
- if (layers[p_layer].navigation_map.is_valid()) {
- return layers[p_layer].navigation_map;
- }
- return RID();
-}
-
-void TileMap::set_y_sort_enabled(bool p_enable) {
- if (is_y_sort_enabled() == p_enable) {
- return;
- }
- Node2D::set_y_sort_enabled(p_enable);
- _clear_internals();
- _recreate_internals();
- emit_signal(SNAME("changed"));
- update_configuration_warnings();
-}
-
-Vector2i TileMap::_coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const {
- int quad_size = get_effective_quadrant_size(p_layer);
-
- // Rounding down, instead of simply rounding towards zero (truncating)
+ // Rounding down, instead of simply rounding towards zero (truncating).
return Vector2i(
p_coords.x > 0 ? p_coords.x / quad_size : (p_coords.x - (quad_size - 1)) / quad_size,
p_coords.y > 0 ? p_coords.y / quad_size : (p_coords.y - (quad_size - 1)) / quad_size);
}
-HashMap<Vector2i, TileMapQuadrant>::Iterator TileMap::_create_quadrant(int p_layer, const Vector2i &p_qk) {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr);
-
+HashMap<Vector2i, TileMapQuadrant>::Iterator TileMapLayer::_create_quadrant(const Vector2i &p_qk) {
TileMapQuadrant q;
- q.layer = p_layer;
q.coords = p_qk;
rect_cache_dirty = true;
@@ -833,156 +59,32 @@ HashMap<Vector2i, TileMapQuadrant>::Iterator TileMap::_create_quadrant(int p_lay
RenderingServer *rs = RenderingServer::get_singleton();
q.debug_canvas_item = rs->canvas_item_create();
rs->canvas_item_set_z_index(q.debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1);
- rs->canvas_item_set_parent(q.debug_canvas_item, get_canvas_item());
+ rs->canvas_item_set_parent(q.debug_canvas_item, tile_map_node->get_canvas_item());
- // Call the create_quadrant method on plugins
+ // Call the create_quadrant method on plugins.
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
if (tile_set.is_valid()) {
_rendering_create_quadrant(&q);
}
- return layers[p_layer].quadrant_map.insert(p_qk, q);
+ return quadrant_map.insert(p_qk, q);
}
-void TileMap::_make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) {
+void TileMapLayer::_make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) {
// Make the given quadrant dirty, then trigger an update later.
TileMapQuadrant &q = Q->value;
if (!q.dirty_list_element.in_list()) {
- layers[q.layer].dirty_quadrant_list.add(&q.dirty_list_element);
- }
- _queue_update_dirty_quadrants();
-}
-
-void TileMap::_make_all_quadrants_dirty() {
- // Make all quandrants dirty, then trigger an update later.
- for (TileMapLayer &layer : layers) {
- for (KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) {
- if (!E.value.dirty_list_element.in_list()) {
- layer.dirty_quadrant_list.add(&E.value.dirty_list_element);
- }
- }
- }
- _queue_update_dirty_quadrants();
-}
-
-void TileMap::_queue_update_dirty_quadrants() {
- if (pending_update || !is_inside_tree()) {
- return;
- }
- pending_update = true;
- call_deferred(SNAME("_update_dirty_quadrants"));
-}
-
-void TileMap::_update_dirty_quadrants() {
- if (!pending_update) {
- return;
- }
- if (!is_inside_tree() || !tile_set.is_valid()) {
- pending_update = false;
- return;
- }
-
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- SelfList<TileMapQuadrant>::List &dirty_quadrant_list = layers[layer].dirty_quadrant_list;
-
- // Update the coords cache.
- for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
- q->self()->map_to_local.clear();
- q->self()->local_to_map.clear();
- for (const Vector2i &E : q->self()->cells) {
- Vector2i pk = E;
- Vector2 pk_local_coords = map_to_local(pk);
- q->self()->map_to_local[pk] = pk_local_coords;
- q->self()->local_to_map[pk_local_coords] = pk;
- }
- }
-
- // Find TileData that need a runtime modification.
- _build_runtime_update_tile_data(dirty_quadrant_list);
-
- // Call the update_dirty_quadrant method on plugins.
- _rendering_update_dirty_quadrants(dirty_quadrant_list);
- _physics_update_dirty_quadrants(dirty_quadrant_list);
- _navigation_update_dirty_quadrants(dirty_quadrant_list);
- _scenes_update_dirty_quadrants(dirty_quadrant_list);
-
- // Redraw the debug canvas_items.
- RenderingServer *rs = RenderingServer::get_singleton();
- for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
- rs->canvas_item_clear(q->self()->debug_canvas_item);
- Transform2D xform;
- xform.set_origin(map_to_local(q->self()->coords * get_effective_quadrant_size(layer)));
- rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform);
-
- _rendering_draw_quadrant_debug(q->self());
- _physics_draw_quadrant_debug(q->self());
- _navigation_draw_quadrant_debug(q->self());
- _scenes_draw_quadrant_debug(q->self());
- }
-
- // Clear the list
- while (dirty_quadrant_list.first()) {
- // Clear the runtime tile data.
- for (const KeyValue<Vector2i, TileData *> &kv : dirty_quadrant_list.first()->self()->runtime_tile_data_cache) {
- memdelete(kv.value);
- }
-
- dirty_quadrant_list.remove(dirty_quadrant_list.first());
- }
+ dirty_quadrant_list.add(&q.dirty_list_element);
}
-
- pending_update = false;
-
- _recompute_rect_cache();
+ tile_map_node->queue_update_dirty_quadrants();
}
-void TileMap::_recreate_layer_internals(int p_layer) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
- // Make sure that _clear_internals() was called prior.
- ERR_FAIL_COND_MSG(layers[p_layer].quadrant_map.size() > 0, "TileMap layer " + itos(p_layer) + " had a non-empty quadrant map.");
-
- if (!layers[p_layer].enabled) {
- return;
- }
-
- // Update the layer internals.
- _rendering_update_layer(p_layer);
-
- // Update the layer internal navigation maps.
- _navigation_update_layer(p_layer);
-
- // Recreate the quadrants.
- const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
- for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
- Vector2i qk = _coords_to_quadrant_coords(p_layer, Vector2i(E.key.x, E.key.y));
-
- HashMap<Vector2i, TileMapQuadrant>::Iterator Q = layers[p_layer].quadrant_map.find(qk);
- if (!Q) {
- Q = _create_quadrant(p_layer, qk);
- layers[p_layer].dirty_quadrant_list.add(&Q->value.dirty_list_element);
- }
-
- Vector2i pk = E.key;
- Q->value.cells.insert(pk);
-
- _make_quadrant_dirty(Q);
- }
-
- _queue_update_dirty_quadrants();
-}
-
-void TileMap::_recreate_internals() {
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- _recreate_layer_internals(layer);
- }
-}
-
-void TileMap::_erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) {
+void TileMapLayer::_erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) {
// Remove a quadrant.
TileMapQuadrant *q = &(Q->value);
// Call the cleanup_quadrant method on plugins.
- if (tile_set.is_valid()) {
+ if (tile_map_node->get_tileset().is_valid()) {
_rendering_cleanup_quadrant(q);
_physics_cleanup_quadrant(q);
_navigation_cleanup_quadrant(q);
@@ -991,217 +93,42 @@ void TileMap::_erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) {
// Remove the quadrant from the dirty_list if it is there.
if (q->dirty_list_element.in_list()) {
- layers[q->layer].dirty_quadrant_list.remove(&(q->dirty_list_element));
+ dirty_quadrant_list.remove(&(q->dirty_list_element));
}
// Free the debug canvas item.
RenderingServer *rs = RenderingServer::get_singleton();
rs->free(q->debug_canvas_item);
- layers[q->layer].quadrant_map.remove(Q);
+ quadrant_map.remove(Q);
rect_cache_dirty = true;
}
-void TileMap::_clear_layer_internals(int p_layer) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
- // Clear quadrants.
- while (layers[p_layer].quadrant_map.size()) {
- _erase_quadrant(layers[p_layer].quadrant_map.begin());
- }
-
- // Clear the layers internals.
- _rendering_cleanup_layer(p_layer);
-
- // Clear the layers internal navigation maps.
- _navigation_cleanup_layer(p_layer);
-
- // Clear the dirty quadrants list.
- while (layers[p_layer].dirty_quadrant_list.first()) {
- layers[p_layer].dirty_quadrant_list.remove(layers[p_layer].dirty_quadrant_list.first());
- }
-}
-
-void TileMap::_clear_internals() {
- // Clear quadrants.
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- _clear_layer_internals(layer);
- }
-}
-
-void TileMap::_recompute_rect_cache() {
- // Compute the displayed area of the tilemap.
-#ifdef DEBUG_ENABLED
-
- if (!rect_cache_dirty) {
- return;
- }
-
- Rect2 r_total;
- bool first = true;
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
- Rect2 r;
- r.position = map_to_local(E.key * get_effective_quadrant_size(layer));
- r.expand_to(map_to_local((E.key + Vector2i(1, 0)) * get_effective_quadrant_size(layer)));
- r.expand_to(map_to_local((E.key + Vector2i(1, 1)) * get_effective_quadrant_size(layer)));
- r.expand_to(map_to_local((E.key + Vector2i(0, 1)) * get_effective_quadrant_size(layer)));
- if (first) {
- r_total = r;
- first = false;
- } else {
- r_total = r_total.merge(r);
- }
- }
- }
-
- bool changed = rect_cache != r_total;
-
- rect_cache = r_total;
-
- item_rect_changed(changed);
-
- rect_cache_dirty = false;
-#endif
-}
-
/////////////////////////////// Rendering //////////////////////////////////////
-void TileMap::_rendering_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_ENTER_CANVAS: {
- bool node_visible = is_visible_in_tree();
- for (TileMapLayer &layer : layers) {
- for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) {
- TileMapQuadrant &q = E_quadrant.value;
- for (const KeyValue<Vector2i, RID> &kv : q.occluders) {
- Transform2D xform;
- xform.set_origin(map_to_local(kv.key));
- RS::get_singleton()->canvas_light_occluder_attach_to_canvas(kv.value, get_canvas());
- RS::get_singleton()->canvas_light_occluder_set_transform(kv.value, get_global_transform() * xform);
- RS::get_singleton()->canvas_light_occluder_set_enabled(kv.value, node_visible);
- }
- }
- }
- } break;
-
- case NOTIFICATION_VISIBILITY_CHANGED: {
- bool node_visible = is_visible_in_tree();
- for (TileMapLayer &layer : layers) {
- for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) {
- TileMapQuadrant &q = E_quadrant.value;
-
- // Update occluders transform.
- for (const KeyValue<Vector2, Vector2i> &E_cell : q.local_to_map) {
- Transform2D xform;
- xform.set_origin(E_cell.key);
- for (const KeyValue<Vector2i, RID> &kv : q.occluders) {
- RS::get_singleton()->canvas_light_occluder_set_enabled(kv.value, node_visible);
- }
- }
- }
- }
- } break;
-
- case NOTIFICATION_TRANSFORM_CHANGED: {
- if (!is_inside_tree()) {
- return;
- }
- for (TileMapLayer &layer : layers) {
- for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) {
- TileMapQuadrant &q = E_quadrant.value;
-
- // Update occluders transform.
- for (const KeyValue<Vector2i, RID> &kv : q.occluders) {
- Transform2D xform;
- xform.set_origin(map_to_local(kv.key));
- RenderingServer::get_singleton()->canvas_light_occluder_set_transform(kv.value, get_global_transform() * xform);
- }
- }
- }
- } break;
-
- case NOTIFICATION_DRAW: {
- if (tile_set.is_valid()) {
- RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), is_y_sort_enabled());
- }
- } break;
-
- case NOTIFICATION_EXIT_CANVAS: {
- for (TileMapLayer &layer : layers) {
- for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) {
- TileMapQuadrant &q = E_quadrant.value;
- for (const KeyValue<Vector2i, RID> &kv : q.occluders) {
- RS::get_singleton()->canvas_light_occluder_attach_to_canvas(kv.value, RID());
- }
- }
- }
- } break;
- }
-}
-
-void TileMap::_navigation_update_layer(int p_layer) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
- ERR_FAIL_NULL(NavigationServer2D::get_singleton());
-
- if (!layers[p_layer].navigation_map.is_valid()) {
- if (p_layer == 0 && is_inside_tree()) {
- // Use the default World2D navigation map for the first layer when empty.
- layers[p_layer].navigation_map = get_world_2d()->get_navigation_map();
- layers[p_layer].uses_world_navigation_map = true;
- } else {
- RID new_layer_map = NavigationServer2D::get_singleton()->map_create();
- NavigationServer2D::get_singleton()->map_set_active(new_layer_map, true);
- layers[p_layer].navigation_map = new_layer_map;
- layers[p_layer].uses_world_navigation_map = false;
- }
- }
-}
-
-void TileMap::_navigation_cleanup_layer(int p_layer) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
- ERR_FAIL_NULL(NavigationServer2D::get_singleton());
-
- if (layers[p_layer].navigation_map.is_valid()) {
- if (layers[p_layer].uses_world_navigation_map) {
- // Do not delete the World2D default navigation map.
- return;
- }
- NavigationServer2D::get_singleton()->free(layers[p_layer].navigation_map);
- layers[p_layer].navigation_map = RID();
- }
-}
-
-void TileMap::_rendering_update_layer(int p_layer) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
+void TileMapLayer::_rendering_update() {
RenderingServer *rs = RenderingServer::get_singleton();
- if (!layers[p_layer].canvas_item.is_valid()) {
+ if (!canvas_item.is_valid()) {
RID ci = rs->canvas_item_create();
- rs->canvas_item_set_parent(ci, get_canvas_item());
-
- /*Transform2D xform;
- xform.set_origin(Vector2(0, p_layer));
- rs->canvas_item_set_transform(ci, xform);*/
- rs->canvas_item_set_draw_index(ci, p_layer - (int64_t)0x80000000);
-
- layers[p_layer].canvas_item = ci;
- }
- RID &ci = layers[p_layer].canvas_item;
- rs->canvas_item_set_sort_children_by_y(ci, layers[p_layer].y_sort_enabled);
- rs->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid());
- rs->canvas_item_set_z_index(ci, layers[p_layer].z_index);
- rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter_in_tree()));
- rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat_in_tree()));
- rs->canvas_item_set_light_mask(ci, get_light_mask());
-
- Color layer_modulate = get_layer_modulate(p_layer);
- if (selected_layer >= 0 && p_layer != selected_layer) {
- int z1 = get_layer_z_index(p_layer);
- int z2 = get_layer_z_index(selected_layer);
- if (z1 < z2 || (z1 == z2 && p_layer < selected_layer)) {
+ rs->canvas_item_set_parent(ci, tile_map_node->get_canvas_item());
+ rs->canvas_item_set_draw_index(ci, layer_index_in_tile_map_node - (int64_t)0x80000000);
+ canvas_item = ci;
+ }
+ RID &ci = canvas_item;
+ rs->canvas_item_set_sort_children_by_y(ci, y_sort_enabled);
+ rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid());
+ rs->canvas_item_set_z_index(ci, z_index);
+ rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree()));
+ rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree()));
+ rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask());
+
+ Color layer_modulate = modulate;
+ int selected_layer = tile_map_node->get_selected_layer();
+ if (selected_layer >= 0 && layer_index_in_tile_map_node != selected_layer) {
+ int z_selected = tile_map_node->get_layer_z_index(selected_layer);
+ if (z_index < z_selected || (z_index == z_selected && layer_index_in_tile_map_node < selected_layer)) {
layer_modulate = layer_modulate.darkened(0.5);
- } else if (z1 > z2 || (z1 == z2 && p_layer > selected_layer)) {
+ } else if (z_index > z_selected || (z_index == z_selected && layer_index_in_tile_map_node > selected_layer)) {
layer_modulate = layer_modulate.darkened(0.5);
layer_modulate.a *= 0.3;
}
@@ -1209,22 +136,21 @@ void TileMap::_rendering_update_layer(int p_layer) {
rs->canvas_item_set_modulate(ci, layer_modulate);
}
-void TileMap::_rendering_cleanup_layer(int p_layer) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
+void TileMapLayer::_rendering_cleanup() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer *rs = RenderingServer::get_singleton();
- if (layers[p_layer].canvas_item.is_valid()) {
- rs->free(layers[p_layer].canvas_item);
- layers[p_layer].canvas_item = RID();
+ if (canvas_item.is_valid()) {
+ rs->free(canvas_item);
+ canvas_item = RID();
}
}
-void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
- ERR_FAIL_COND(!is_inside_tree());
+void TileMapLayer::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ ERR_FAIL_COND(!tile_map_node->is_inside_tree());
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
- bool node_visible = is_visible_in_tree();
+ bool node_visible = tile_map_node->is_visible_in_tree();
SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
while (q_list_element) {
@@ -1251,7 +177,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
// Iterate over the cells of the quadrant.
for (const KeyValue<Vector2, Vector2i> &E_cell : q.local_to_map) {
- TileMapCell c = get_cell(q.layer, E_cell.value, true);
+ TileMapCell c = get_cell(E_cell.value, true);
TileSetSource *source;
if (tile_set->has_source(c.source_id)) {
@@ -1275,10 +201,10 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
int tile_z_index = tile_data->get_z_index();
// Quandrant pos.
- Vector2 tile_position = map_to_local(q.coords * get_effective_quadrant_size(q.layer));
- if (is_y_sort_enabled() && layers[q.layer].y_sort_enabled) {
+ Vector2 tile_position = tile_map_node->map_to_local(q.coords * get_effective_quadrant_size());
+ if (tile_map_node->is_y_sort_enabled() && y_sort_enabled) {
// When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem.
- tile_position.y += layers[q.layer].y_sort_origin + tile_data->get_y_sort_origin();
+ tile_position.y += y_sort_origin + tile_data->get_y_sort_origin();
}
// --- CanvasItems ---
@@ -1292,19 +218,19 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
if (mat.is_valid()) {
rs->canvas_item_set_material(ci, mat->get_rid());
}
- rs->canvas_item_set_parent(ci, layers[q.layer].canvas_item);
- rs->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid());
+ rs->canvas_item_set_parent(ci, canvas_item);
+ rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid());
Transform2D xform;
xform.set_origin(tile_position);
rs->canvas_item_set_transform(ci, xform);
- rs->canvas_item_set_light_mask(ci, get_light_mask());
+ rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask());
rs->canvas_item_set_z_as_relative_to_parent(ci, true);
rs->canvas_item_set_z_index(ci, tile_z_index);
- rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter_in_tree()));
- rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat_in_tree()));
+ rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree()));
+ rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree()));
q.canvas_items.push_back(ci);
@@ -1317,21 +243,17 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
ci = prev_ci;
}
- Vector2 p_position = E_cell.key - tile_position;
- Vector2 p_atlas_coords = c.get_atlas_coords();
-
// Random animation offset.
- real_t p_random_animation_offset = 0.0;
- if (atlas_source->get_tile_animation_mode(p_atlas_coords) != TileSetAtlasSource::TILE_ANIMATION_MODE_DEFAULT) {
+ real_t random_animation_offset = 0.0;
+ if (atlas_source->get_tile_animation_mode(c.get_atlas_coords()) != TileSetAtlasSource::TILE_ANIMATION_MODE_DEFAULT) {
Array to_hash;
- to_hash.push_back(p_position);
- to_hash.push_back(q.layer);
- to_hash.push_back(Variant(this));
- p_random_animation_offset = RandomPCG(to_hash.hash()).randf();
+ to_hash.push_back(E_cell.key);
+ to_hash.push_back(get_instance_id()); // Use instance id as a random hash
+ random_animation_offset = RandomPCG(to_hash.hash()).randf();
}
// Drawing the tile in the canvas item.
- draw_tile(ci, p_position, tile_set, c.source_id, p_atlas_coords, c.alternative_tile, -1, get_self_modulate(), tile_data, p_random_animation_offset);
+ tile_map_node->draw_tile(ci, E_cell.key - tile_position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, tile_map_node->get_self_modulate(), tile_data, random_animation_offset);
// --- Occluders ---
for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) {
@@ -1340,9 +262,9 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
if (tile_data->get_occluder(i).is_valid()) {
RID occluder_id = rs->canvas_light_occluder_create();
rs->canvas_light_occluder_set_enabled(occluder_id, node_visible);
- rs->canvas_light_occluder_set_transform(occluder_id, get_global_transform() * xform);
+ rs->canvas_light_occluder_set_transform(occluder_id, tile_map_node->get_global_transform() * xform);
rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid());
- rs->canvas_light_occluder_attach_to_canvas(occluder_id, get_canvas());
+ rs->canvas_light_occluder_attach_to_canvas(occluder_id, tile_map_node->get_canvas());
rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i));
q.occluders[E_cell.value] = occluder_id;
}
@@ -1355,36 +277,35 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
q_list_element = q_list_element->next();
}
- // Reset the drawing indices
+ // Reset the drawing indices.
if (_rendering_quadrant_order_dirty) {
int index = -(int64_t)0x80000000; //always must be drawn below children.
- for (TileMapLayer &layer : layers) {
- // Sort the quadrants coords per local coordinates.
- RBMap<Vector2, Vector2i, TileMapQuadrant::CoordsWorldComparator> local_to_map;
- for (const KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) {
- local_to_map[map_to_local(E.key)] = E.key;
- }
+ // Sort the quadrants coords per local coordinates.
+ RBMap<Vector2, Vector2i, TileMapQuadrant::CoordsWorldComparator> local_to_map;
+ for (const KeyValue<Vector2i, TileMapQuadrant> &E : quadrant_map) {
+ local_to_map[tile_map_node->map_to_local(E.key)] = E.key;
+ }
- // Sort the quadrants.
- for (const KeyValue<Vector2, Vector2i> &E : local_to_map) {
- TileMapQuadrant &q = layer.quadrant_map[E.value];
- for (const RID &ci : q.canvas_items) {
- RS::get_singleton()->canvas_item_set_draw_index(ci, index++);
- }
+ // Sort the quadrants.
+ for (const KeyValue<Vector2, Vector2i> &E : local_to_map) {
+ TileMapQuadrant &q = quadrant_map[E.value];
+ for (const RID &ci : q.canvas_items) {
+ RS::get_singleton()->canvas_item_set_draw_index(ci, index++);
}
}
_rendering_quadrant_order_dirty = false;
}
}
-void TileMap::_rendering_create_quadrant(TileMapQuadrant *p_quadrant) {
+void TileMapLayer::_rendering_create_quadrant(TileMapQuadrant *p_quadrant) {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
_rendering_quadrant_order_dirty = true;
}
-void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
+void TileMapLayer::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
ERR_FAIL_NULL(RenderingServer::get_singleton());
// Free the canvas items.
for (const RID &ci : p_quadrant->canvas_items) {
@@ -1399,7 +320,8 @@ void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
p_quadrant->occluders.clear();
}
-void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
+void TileMapLayer::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
if (!Engine::get_singleton()->is_editor_hint()) {
@@ -1408,9 +330,9 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
// Draw a placeholder for tiles needing one.
RenderingServer *rs = RenderingServer::get_singleton();
- Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+ Vector2 quadrant_pos = tile_map_node->map_to_local(p_quadrant->coords * get_effective_quadrant_size());
for (const Vector2i &E_cell : p_quadrant->cells) {
- const TileMapCell &c = get_cell(p_quadrant->layer, E_cell, true);
+ const TileMapCell &c = get_cell(E_cell, true);
TileSetSource *source;
if (tile_set->has_source(c.source_id)) {
@@ -1440,7 +362,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
// Draw a placeholder tile.
Transform2D cell_to_quadrant;
- cell_to_quadrant.set_origin(map_to_local(E_cell) - quadrant_pos);
+ cell_to_quadrant.set_origin(tile_map_node->map_to_local(E_cell) - quadrant_pos);
rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, cell_to_quadrant);
rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
}
@@ -1449,167 +371,17 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
}
}
-void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_animation_offset) {
- ERR_FAIL_COND(!p_tile_set.is_valid());
- ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id));
- ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords));
- ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile));
- TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id);
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- // Check for the frame.
- if (p_frame >= 0) {
- ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords));
- }
-
- // Get the texture.
- Ref<Texture2D> tex = atlas_source->get_runtime_texture();
- if (!tex.is_valid()) {
- return;
- }
-
- // Check if we are in the texture, return otherwise.
- Vector2i grid_size = atlas_source->get_atlas_grid_size();
- if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) {
- return;
- }
-
- // Get tile data.
- const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile);
-
- // Get the tile modulation.
- Color modulate = tile_data->get_modulate() * p_modulation;
-
- // Compute the offset.
- Vector2 tile_offset = tile_data->get_texture_origin();
-
- // Get destination rect.
- Rect2 dest_rect;
- dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size;
- dest_rect.size.x += FP_ADJUST;
- dest_rect.size.y += FP_ADJUST;
-
- bool transpose = tile_data->get_transpose();
- if (transpose) {
- dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
- } else {
- dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset);
- }
-
- if (tile_data->get_flip_h()) {
- dest_rect.size.x = -dest_rect.size.x;
- }
-
- if (tile_data->get_flip_v()) {
- dest_rect.size.y = -dest_rect.size.y;
- }
-
- // Draw the tile.
- if (p_frame >= 0) {
- Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame);
- tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
- } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) {
- Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0);
- tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
- } else {
- real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords);
- real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed;
- real_t time = 0.0;
- for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) {
- real_t frame_duration = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame) / speed;
- RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, time, time + frame_duration, p_animation_offset);
-
- Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame);
- tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
-
- time += frame_duration;
- }
- RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0);
- }
- }
-}
-
/////////////////////////////// Physics //////////////////////////////////////
-void TileMap::_physics_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- bool in_editor = false;
-#ifdef TOOLS_ENABLED
- in_editor = Engine::get_singleton()->is_editor_hint();
-#endif
- if (is_inside_tree() && collision_animatable && !in_editor) {
- // Update transform on the physics tick when in animatable mode.
- last_valid_transform = new_transform;
- set_notify_local_transform(false);
- set_global_transform(new_transform);
- set_notify_local_transform(true);
- }
- } break;
-
- case NOTIFICATION_TRANSFORM_CHANGED: {
- bool in_editor = false;
-#ifdef TOOLS_ENABLED
- in_editor = Engine::get_singleton()->is_editor_hint();
-#endif
- if (is_inside_tree() && (!collision_animatable || in_editor)) {
- // Update the new transform directly if we are not in animatable mode.
- Transform2D gl_transform = get_global_transform();
- for (TileMapLayer &layer : layers) {
- for (KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) {
- TileMapQuadrant &q = E.value;
-
- for (RID body : q.bodies) {
- Transform2D xform;
- xform.set_origin(map_to_local(bodies_coords[body]));
- xform = gl_transform * xform;
- PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
- }
- }
- }
- }
- } break;
-
- case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- bool in_editor = false;
-#ifdef TOOLS_ENABLED
- in_editor = Engine::get_singleton()->is_editor_hint();
-#endif
- if (is_inside_tree() && !in_editor && collision_animatable) {
- // Only active when animatable. Send the new transform to the physics...
- new_transform = get_global_transform();
- for (TileMapLayer &layer : layers) {
- for (KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) {
- TileMapQuadrant &q = E.value;
-
- for (RID body : q.bodies) {
- Transform2D xform;
- xform.set_origin(map_to_local(bodies_coords[body]));
- xform = new_transform * xform;
-
- PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
- }
- }
- }
-
- // ... but then revert changes.
- set_notify_local_transform(false);
- set_global_transform(last_valid_transform);
- set_notify_local_transform(true);
- }
- } break;
- }
-}
-
-void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
- ERR_FAIL_COND(!is_inside_tree());
+void TileMapLayer::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ ERR_FAIL_COND(!tile_map_node->is_inside_tree());
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
- Transform2D gl_transform = get_global_transform();
- last_valid_transform = gl_transform;
- new_transform = gl_transform;
+ Transform2D gl_transform = tile_map_node->get_global_transform();
+
PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
- RID space = get_world_2d()->get_space();
+ RID space = tile_map_node->get_world_2d()->get_space();
SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
while (q_list_element) {
@@ -1624,7 +396,7 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
// Recreate bodies and shapes.
for (const Vector2i &E_cell : q.cells) {
- TileMapCell c = get_cell(q.layer, E_cell, true);
+ TileMapCell c = get_cell(E_cell, true);
TileSetSource *source;
if (tile_set->has_source(c.source_id)) {
@@ -1650,16 +422,15 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
// Create the body.
RID body = ps->body_create();
bodies_coords[body] = E_cell;
- bodies_layers[body] = q.layer;
- ps->body_set_mode(body, collision_animatable ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC);
+ ps->body_set_mode(body, tile_map_node->is_collision_animatable() ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC);
ps->body_set_space(body, space);
Transform2D xform;
- xform.set_origin(map_to_local(E_cell));
+ xform.set_origin(tile_map_node->map_to_local(E_cell));
xform = gl_transform * xform;
ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
- ps->body_attach_object_instance_id(body, get_instance_id());
+ ps->body_attach_object_instance_id(body, tile_map_node->get_instance_id());
ps->body_set_collision_layer(body, physics_layer);
ps->body_set_collision_mask(body, physics_mask);
ps->body_set_pickable(body, false);
@@ -1701,29 +472,29 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
}
}
-void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
+void TileMapLayer::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
// Remove a quadrant.
ERR_FAIL_NULL(PhysicsServer2D::get_singleton());
for (RID body : p_quadrant->bodies) {
bodies_coords.erase(body);
- bodies_layers.erase(body);
PhysicsServer2D::get_singleton()->free(body);
}
p_quadrant->bodies.clear();
}
-void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
+void TileMapLayer::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
// Draw the debug collision shapes.
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
- if (!get_tree()) {
+ if (!tile_map_node->get_tree()) {
return;
}
bool show_collision = false;
- switch (collision_visibility_mode) {
+ switch (tile_map_node->get_collision_visibility_mode()) {
case TileMap::VISIBILITY_MODE_DEFAULT:
- show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_collisions_hint());
+ show_collision = !Engine::get_singleton()->is_editor_hint() && tile_map_node->get_tree()->is_debugging_collisions_hint();
break;
case TileMap::VISIBILITY_MODE_FORCE_HIDE:
show_collision = false;
@@ -1739,14 +510,14 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
RenderingServer *rs = RenderingServer::get_singleton();
PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
- Color debug_collision_color = get_tree()->get_debug_collisions_color();
+ Color debug_collision_color = tile_map_node->get_tree()->get_debug_collisions_color();
Vector<Color> color;
color.push_back(debug_collision_color);
- Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+ Vector2 quadrant_pos = tile_map_node->map_to_local(p_quadrant->coords * get_effective_quadrant_size());
Transform2D quadrant_to_local;
quadrant_to_local.set_origin(quadrant_pos);
- Transform2D global_to_quadrant = (get_global_transform() * quadrant_to_local).affine_inverse();
+ Transform2D global_to_quadrant = (tile_map_node->get_global_transform() * quadrant_to_local).affine_inverse();
for (RID body : p_quadrant->bodies) {
Transform2D body_to_quadrant = global_to_quadrant * Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM));
@@ -1767,36 +538,12 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
/////////////////////////////// Navigation //////////////////////////////////////
-void TileMap::_navigation_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_TRANSFORM_CHANGED: {
- if (is_inside_tree()) {
- for (TileMapLayer &layer : layers) {
- Transform2D tilemap_xform = get_global_transform();
- for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) {
- TileMapQuadrant &q = E_quadrant.value;
- for (const KeyValue<Vector2i, Vector<RID>> &E_region : q.navigation_regions) {
- for (const RID &region : E_region.value) {
- if (!region.is_valid()) {
- continue;
- }
- Transform2D tile_transform;
- tile_transform.set_origin(map_to_local(E_region.key));
- NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
- }
- }
- }
- }
- }
- } break;
- }
-}
-
-void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
- ERR_FAIL_COND(!is_inside_tree());
+void TileMapLayer::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ ERR_FAIL_COND(!tile_map_node->is_inside_tree());
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
- Transform2D tilemap_xform = get_global_transform();
+ Transform2D tilemap_xform = tile_map_node->get_global_transform();
SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
while (q_list_element) {
TileMapQuadrant &q = *q_list_element->self();
@@ -1815,7 +562,7 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
// Get the navigation polygons and create regions.
for (const Vector2i &E_cell : q.cells) {
- TileMapCell c = get_cell(q.layer, E_cell, true);
+ TileMapCell c = get_cell(E_cell, true);
TileSetSource *source;
if (tile_set->has_source(c.source_id)) {
@@ -1835,24 +582,21 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
}
q.navigation_regions[E_cell].resize(tile_set->get_navigation_layers_count());
- for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
- if (layer_index >= (int)layers.size() || !layers[layer_index].navigation_map.is_valid()) {
- continue;
- }
+ for (int navigation_layer_index = 0; navigation_layer_index < tile_set->get_navigation_layers_count(); navigation_layer_index++) {
Ref<NavigationPolygon> navigation_polygon;
- navigation_polygon = tile_data->get_navigation_polygon(layer_index);
+ navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index);
if (navigation_polygon.is_valid()) {
Transform2D tile_transform;
- tile_transform.set_origin(map_to_local(E_cell));
+ tile_transform.set_origin(tile_map_node->map_to_local(E_cell));
RID region = NavigationServer2D::get_singleton()->region_create();
- NavigationServer2D::get_singleton()->region_set_owner_id(region, get_instance_id());
- NavigationServer2D::get_singleton()->region_set_map(region, layers[layer_index].navigation_map);
+ NavigationServer2D::get_singleton()->region_set_owner_id(region, tile_map_node->get_instance_id());
+ NavigationServer2D::get_singleton()->region_set_map(region, navigation_map);
NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
- NavigationServer2D::get_singleton()->region_set_navigation_layers(region, tile_set->get_navigation_layer_layers(layer_index));
+ NavigationServer2D::get_singleton()->region_set_navigation_layers(region, tile_set->get_navigation_layer_layers(navigation_layer_index));
NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, navigation_polygon);
- q.navigation_regions[E_cell].write[layer_index] = region;
+ q.navigation_regions[E_cell].write[navigation_layer_index] = region;
}
}
}
@@ -1863,7 +607,37 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
}
}
-void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
+void TileMapLayer::_navigation_update() {
+ ERR_FAIL_NULL(NavigationServer2D::get_singleton());
+
+ if (!navigation_map.is_valid()) {
+ if (layer_index_in_tile_map_node == 0 && tile_map_node->is_inside_tree()) {
+ // Use the default World2D navigation map for the first layer when empty.
+ navigation_map = tile_map_node->get_world_2d()->get_navigation_map();
+ uses_world_navigation_map = true;
+ } else {
+ RID new_layer_map = NavigationServer2D::get_singleton()->map_create();
+ NavigationServer2D::get_singleton()->map_set_active(new_layer_map, true);
+ navigation_map = new_layer_map;
+ uses_world_navigation_map = false;
+ }
+ }
+}
+
+void TileMapLayer::_navigation_cleanup() {
+ ERR_FAIL_NULL(NavigationServer2D::get_singleton());
+
+ if (navigation_map.is_valid()) {
+ if (uses_world_navigation_map) {
+ // Do not delete the World2D default navigation map.
+ return;
+ }
+ NavigationServer2D::get_singleton()->free(navigation_map);
+ navigation_map = RID();
+ }
+}
+
+void TileMapLayer::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
// Clear navigation shapes in the quadrant.
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
for (const KeyValue<Vector2i, Vector<RID>> &E : p_quadrant->navigation_regions) {
@@ -1878,18 +652,19 @@ void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
p_quadrant->navigation_regions.clear();
}
-void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
+void TileMapLayer::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
// Draw the debug collision shapes.
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
- if (!get_tree()) {
+ if (!tile_map_node->get_tree()) {
return;
}
bool show_navigation = false;
- switch (navigation_visibility_mode) {
+ switch (tile_map_node->get_navigation_visibility_mode()) {
case TileMap::VISIBILITY_MODE_DEFAULT:
- show_navigation = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint());
+ show_navigation = !Engine::get_singleton()->is_editor_hint() && tile_map_node->get_tree()->is_debugging_navigation_hint();
break;
case TileMap::VISIBILITY_MODE_FORCE_HIDE:
show_navigation = false;
@@ -1914,10 +689,10 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
RandomPCG rand;
- Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+ Vector2 quadrant_pos = tile_map_node->map_to_local(p_quadrant->coords * get_effective_quadrant_size());
for (const Vector2i &E_cell : p_quadrant->cells) {
- TileMapCell c = get_cell(p_quadrant->layer, E_cell, true);
+ TileMapCell c = get_cell(E_cell, true);
TileSetSource *source;
if (tile_set->has_source(c.source_id)) {
@@ -1937,7 +712,7 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
}
Transform2D cell_to_quadrant;
- cell_to_quadrant.set_origin(map_to_local(E_cell) - quadrant_pos);
+ cell_to_quadrant.set_origin(tile_map_node->map_to_local(E_cell) - quadrant_pos);
rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, cell_to_quadrant);
for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
@@ -1989,7 +764,8 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
/////////////////////////////// Scenes //////////////////////////////////////
-void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+void TileMapLayer::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
@@ -1999,7 +775,7 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
// Clear the scenes if instance cache was cleared.
if (instantiated_scenes.is_empty()) {
for (const KeyValue<Vector2i, String> &E : q.scenes) {
- Node *node = get_node_or_null(E.value);
+ Node *node = tile_map_node->get_node_or_null(E.value);
if (node) {
node->queue_free();
}
@@ -2010,16 +786,15 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
// Recreate the scenes.
for (const Vector2i &E_cell : q.cells) {
- Vector3i cell_coords = Vector3i(q.layer, E_cell.x, E_cell.y);
- if (instantiated_scenes.has(cell_coords)) {
+ if (instantiated_scenes.has(E_cell)) {
// Skip scene if the instance was cached (to avoid recreating scenes unnecessarily).
continue;
}
if (!Engine::get_singleton()->is_editor_hint()) {
- instantiated_scenes.insert(cell_coords);
+ instantiated_scenes.insert(E_cell);
}
- const TileMapCell &c = get_cell(q.layer, E_cell, true);
+ const TileMapCell &c = get_cell(E_cell, true);
TileSetSource *source;
if (tile_set->has_source(c.source_id)) {
@@ -2037,13 +812,13 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
Control *scene_as_control = Object::cast_to<Control>(scene);
Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene);
if (scene_as_control) {
- scene_as_control->set_position(map_to_local(E_cell) + scene_as_control->get_position());
+ scene_as_control->set_position(tile_map_node->map_to_local(E_cell) + scene_as_control->get_position());
} else if (scene_as_node2d) {
Transform2D xform;
- xform.set_origin(map_to_local(E_cell));
+ xform.set_origin(tile_map_node->map_to_local(E_cell));
scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform());
}
- add_child(scene);
+ tile_map_node->add_child(scene);
q.scenes[E_cell] = scene->get_name();
}
}
@@ -2054,11 +829,11 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
}
}
-void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
+void TileMapLayer::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
// Clear the scenes if instance cache was cleared.
if (instantiated_scenes.is_empty()) {
for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) {
- Node *node = get_node_or_null(E.value);
+ Node *node = tile_map_node->get_node_or_null(E.value);
if (node) {
node->queue_free();
}
@@ -2067,7 +842,8 @@ void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
}
}
-void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
+void TileMapLayer::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
if (!Engine::get_singleton()->is_editor_hint()) {
@@ -2076,9 +852,9 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
// Draw a placeholder for scenes needing one.
RenderingServer *rs = RenderingServer::get_singleton();
- Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+ Vector2 quadrant_pos = tile_map_node->map_to_local(p_quadrant->coords * get_effective_quadrant_size());
for (const Vector2i &E_cell : p_quadrant->cells) {
- const TileMapCell &c = get_cell(p_quadrant->layer, E_cell, true);
+ const TileMapCell &c = get_cell(E_cell, true);
TileSetSource *source;
if (tile_set->has_source(c.source_id)) {
@@ -2106,7 +882,7 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
// Draw a placeholder tile.
Transform2D cell_to_quadrant;
- cell_to_quadrant.set_origin(map_to_local(E_cell) - quadrant_pos);
+ cell_to_quadrant.set_origin(tile_map_node->map_to_local(E_cell) - quadrant_pos);
rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, cell_to_quadrant);
rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
}
@@ -2115,249 +891,51 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
}
}
-void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
-
- // Set the current cell tile (using integer position).
- HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
- Vector2i pk(p_coords);
- HashMap<Vector2i, TileMapCell>::Iterator E = tile_map.find(pk);
-
- int source_id = p_source_id;
- Vector2i atlas_coords = p_atlas_coords;
- int alternative_tile = p_alternative_tile;
+/////////////////////////////////////////////////////////////////////
- if ((source_id == TileSet::INVALID_SOURCE || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) &&
- (source_id != TileSet::INVALID_SOURCE || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) {
- source_id = TileSet::INVALID_SOURCE;
- atlas_coords = TileSetSource::INVALID_ATLAS_COORDS;
- alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
- }
-
- if (!E && source_id == TileSet::INVALID_SOURCE) {
- return; // Nothing to do, the tile is already empty.
- }
-
- // Get the quadrant
- Vector2i qk = _coords_to_quadrant_coords(p_layer, pk);
-
- HashMap<Vector2i, TileMapQuadrant>::Iterator Q = layers[p_layer].quadrant_map.find(qk);
-
- if (source_id == TileSet::INVALID_SOURCE) {
- // Erase existing cell in the tile map.
- tile_map.erase(pk);
-
- // Erase existing cell in the quadrant.
- ERR_FAIL_COND(!Q);
- TileMapQuadrant &q = Q->value;
-
- q.cells.erase(pk);
-
- // Remove or make the quadrant dirty.
- if (q.cells.size() == 0) {
- _erase_quadrant(Q);
- } else {
- _make_quadrant_dirty(Q);
- }
-
- used_rect_cache_dirty = true;
- } else {
- if (!E) {
- // Insert a new cell in the tile map.
- E = tile_map.insert(pk, TileMapCell());
-
- // Create a new quadrant if needed, then insert the cell if needed.
- if (!Q) {
- Q = _create_quadrant(p_layer, qk);
- }
- TileMapQuadrant &q = Q->value;
- q.cells.insert(pk);
-
- } else {
- ERR_FAIL_COND(!Q); // TileMapQuadrant should exist...
-
- if (E->value.source_id == source_id && E->value.get_atlas_coords() == atlas_coords && E->value.alternative_tile == alternative_tile) {
- return; // Nothing changed.
- }
- }
-
- TileMapCell &c = E->value;
-
- c.source_id = source_id;
- c.set_atlas_coords(atlas_coords);
- c.alternative_tile = alternative_tile;
-
- _make_quadrant_dirty(Q);
- used_rect_cache_dirty = true;
- }
-}
-
-void TileMap::erase_cell(int p_layer, const Vector2i &p_coords) {
- set_cell(p_layer, p_coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
-}
-
-int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSet::INVALID_SOURCE);
-
- // Get a cell source id from position.
- const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
- HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords);
-
- if (!E) {
- return TileSet::INVALID_SOURCE;
- }
-
- if (p_use_proxies && tile_set.is_valid()) {
- Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile);
- return proxyed[0];
- }
-
- return E->value.source_id;
-}
-
-Vector2i TileMap::get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_ATLAS_COORDS);
-
- // Get a cell source id from position
- const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
- HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords);
-
- if (!E) {
- return TileSetSource::INVALID_ATLAS_COORDS;
- }
-
- if (p_use_proxies && tile_set.is_valid()) {
- Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile);
- return proxyed[1];
- }
-
- return E->value.get_atlas_coords();
-}
-
-int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_TILE_ALTERNATIVE);
-
- // Get a cell source id from position
- const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
- HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords);
-
- if (!E) {
- return TileSetSource::INVALID_TILE_ALTERNATIVE;
- }
-
- if (p_use_proxies && tile_set.is_valid()) {
- Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile);
- return proxyed[2];
- }
-
- return E->value.alternative_tile;
-}
-
-TileData *TileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
- int source_id = get_cell_source_id(p_layer, p_coords, p_use_proxies);
- if (source_id == TileSet::INVALID_SOURCE) {
- return nullptr;
+void TileMapLayer::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ if (!tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_use_tile_data_runtime_update) || !tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_tile_data_runtime_update)) {
+ return;
}
- Ref<TileSetAtlasSource> source = tile_set->get_source(source_id);
- if (source.is_valid()) {
- return source->get_tile_data(get_cell_atlas_coords(p_layer, p_coords, p_use_proxies), get_cell_alternative_tile(p_layer, p_coords, p_use_proxies));
- }
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
+ while (q_list_element) {
+ TileMapQuadrant &q = *q_list_element->self();
+ // Iterate over the cells of the quadrant.
+ for (const KeyValue<Vector2, Vector2i> &E_cell : q.local_to_map) {
+ TileMapCell c = get_cell(E_cell.value, true);
- return nullptr;
-}
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
-Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr);
- ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr);
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
- Ref<TileMapPattern> output;
- output.instantiate();
- if (p_coords_array.is_empty()) {
- return output;
- }
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ bool ret = false;
+ if (tile_map_node->GDVIRTUAL_CALL(_use_tile_data_runtime_update, layer_index_in_tile_map_node, E_cell.value, ret) && ret) {
+ TileData *tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
- Vector2i min = Vector2i(p_coords_array[0]);
- for (int i = 1; i < p_coords_array.size(); i++) {
- min = min.min(p_coords_array[i]);
- }
+ // Create the runtime TileData.
+ TileData *tile_data_runtime_use = tile_data->duplicate();
+ tile_data_runtime_use->set_allow_transform(true);
+ q.runtime_tile_data_cache[E_cell.value] = tile_data_runtime_use;
- Vector<Vector2i> coords_in_pattern_array;
- coords_in_pattern_array.resize(p_coords_array.size());
- Vector2i ensure_positive_offset;
- for (int i = 0; i < p_coords_array.size(); i++) {
- Vector2i coords = p_coords_array[i];
- Vector2i coords_in_pattern = coords - min;
- if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) {
- if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) {
- coords_in_pattern.x -= 1;
- if (coords_in_pattern.x < 0) {
- ensure_positive_offset.x = 1;
- }
- } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) {
- coords_in_pattern.y -= 1;
- if (coords_in_pattern.y < 0) {
- ensure_positive_offset.y = 1;
+ tile_map_node->GDVIRTUAL_CALL(_tile_data_runtime_update, layer_index_in_tile_map_node, E_cell.value, tile_data_runtime_use);
}
}
- } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) {
- coords_in_pattern.x += 1;
- } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) {
- coords_in_pattern.y += 1;
- }
}
}
- coords_in_pattern_array.write[i] = coords_in_pattern;
- }
-
- for (int i = 0; i < coords_in_pattern_array.size(); i++) {
- Vector2i coords = p_coords_array[i];
- Vector2i coords_in_pattern = coords_in_pattern_array[i];
- output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(p_layer, coords), get_cell_atlas_coords(p_layer, coords), get_cell_alternative_tile(p_layer, coords));
- }
-
- return output;
-}
-
-Vector2i TileMap::map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) {
- ERR_FAIL_COND_V(p_pattern.is_null(), Vector2i());
- ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i());
-
- Vector2i output = p_position_in_tilemap + p_coords_in_pattern;
- if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) {
- if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) {
- output.x += 1;
- } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) {
- output.y += 1;
- }
- } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) {
- output.x -= 1;
- } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) {
- output.y -= 1;
- }
- }
- }
-
- return output;
-}
-
-void TileMap::set_pattern(int p_layer, const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
- ERR_FAIL_COND(tile_set.is_null());
- ERR_FAIL_COND(p_pattern.is_null());
-
- TypedArray<Vector2i> used_cells = p_pattern->get_used_cells();
- for (int i = 0; i < used_cells.size(); i++) {
- Vector2i coords = map_pattern(p_position, used_cells[i], p_pattern);
- set_cell(p_layer, coords, p_pattern->get_cell_source_id(used_cells[i]), p_pattern->get_cell_atlas_coords(used_cells[i]), p_pattern->get_cell_alternative_tile(used_cells[i]));
+ q_list_element = q_list_element->next();
}
}
-TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) {
+TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
if (!tile_set.is_valid()) {
return TileSet::TerrainsPattern();
}
@@ -2368,8 +946,8 @@ TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int
for (TileSet::TerrainsPattern &terrain_pattern : pattern_set) {
int score = 0;
- // Check the center bit constraint
- TerrainConstraint terrain_constraint = TerrainConstraint(this, p_position, terrain_pattern.get_terrain());
+ // Check the center bit constraint.
+ TerrainConstraint terrain_constraint = TerrainConstraint(tile_map_node, p_position, terrain_pattern.get_terrain());
const RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_constraint);
if (in_set_constraint_element) {
if (in_set_constraint_element->get().get_terrain() != terrain_constraint.get_terrain()) {
@@ -2385,7 +963,7 @@ TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
// Check if the bit is compatible with the constraints.
- TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern.get_terrain_peering_bit(bit));
+ TerrainConstraint terrain_bit_constraint = TerrainConstraint(tile_map_node, p_position, bit, terrain_pattern.get_terrain_peering_bit(bit));
in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
if (in_set_constraint_element) {
if (in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
@@ -2404,7 +982,7 @@ TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int
terrain_pattern_score[terrain_pattern] = score;
}
- // Compute the minimum score
+ // Compute the minimum score.
TileSet::TerrainsPattern min_score_pattern = p_current_pattern;
int min_score = INT32_MAX;
for (KeyValue<TileSet::TerrainsPattern, int> E : terrain_pattern_score) {
@@ -2417,19 +995,20 @@ TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int
return min_score_pattern;
}
-RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
+RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
if (!tile_set.is_valid()) {
return RBSet<TerrainConstraint>();
}
// Compute the constraints needed from the surrounding tiles.
RBSet<TerrainConstraint> output;
- output.insert(TerrainConstraint(this, p_position, p_terrains_pattern.get_terrain()));
+ output.insert(TerrainConstraint(tile_map_node, p_position, p_terrains_pattern.get_terrain()));
for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, side)) {
- TerrainConstraint c = TerrainConstraint(this, p_position, side, p_terrains_pattern.get_terrain_peering_bit(side));
+ TerrainConstraint c = TerrainConstraint(tile_map_node, p_position, side, p_terrains_pattern.get_terrain_peering_bit(side));
output.insert(c);
}
}
@@ -2437,13 +1016,13 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_added_p
return output;
}
-RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted_cells_list(int p_layer, const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const {
+RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
if (!tile_set.is_valid()) {
return RBSet<TerrainConstraint>();
}
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), RBSet<TerrainConstraint>());
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), RBSet<TerrainConstraint>());
// Build a set of dummy constraints to get the constrained points.
RBSet<TerrainConstraint> dummy_constraints;
@@ -2451,7 +1030,7 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over neighbor bits.
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
- dummy_constraints.insert(TerrainConstraint(this, E, bit, -1));
+ dummy_constraints.insert(TerrainConstraint(tile_map_node, E, bit, -1));
}
}
}
@@ -2465,7 +1044,7 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted
HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = E_constraint.get_overlapping_coords_and_peering_bits();
for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) {
TileData *neighbor_tile_data = nullptr;
- TileMapCell neighbor_cell = get_cell(p_layer, E_overlapping.key);
+ TileMapCell neighbor_cell = get_cell(E_overlapping.key);
if (neighbor_cell.source_id != TileSet::INVALID_SOURCE) {
Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id);
Ref<TileSetAtlasSource> atlas_source = source;
@@ -2504,10 +1083,10 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted
}
}
- // Add the centers as constraints
+ // Add the centers as constraints.
for (Vector2i E_coords : p_painted) {
TileData *tile_data = nullptr;
- TileMapCell cell = get_cell(p_layer, E_coords);
+ TileMapCell cell = get_cell(E_coords);
if (cell.source_id != TileSet::INVALID_SOURCE) {
Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
Ref<TileSetAtlasSource> atlas_source = source;
@@ -2518,14 +1097,54 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted
int terrain = (tile_data && tile_data->get_terrain_set() == p_terrain_set) ? tile_data->get_terrain() : -1;
if (!p_ignore_empty_terrains || terrain >= 0) {
- constraints.insert(TerrainConstraint(this, E_coords, terrain));
+ constraints.insert(TerrainConstraint(tile_map_node, E_coords, terrain));
}
}
return constraints;
}
-HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(int p_layer, const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) {
+void TileMapLayer::set_tile_map(TileMap *p_tile_map) {
+ tile_map_node = p_tile_map;
+}
+
+void TileMapLayer::set_layer_index_in_tile_map_node(int p_index) {
+ layer_index_in_tile_map_node = p_index;
+}
+
+Rect2 TileMapLayer::get_rect(bool &r_changed) const {
+ // Compute the displayed area of the tilemap.
+ r_changed = false;
+#ifdef DEBUG_ENABLED
+
+ if (rect_cache_dirty) {
+ Rect2 r_total;
+ bool first = true;
+ for (const KeyValue<Vector2i, TileMapQuadrant> &E : quadrant_map) {
+ Rect2 r;
+ r.position = tile_map_node->map_to_local(E.key * get_effective_quadrant_size());
+ r.expand_to(tile_map_node->map_to_local((E.key + Vector2i(1, 0)) * get_effective_quadrant_size()));
+ r.expand_to(tile_map_node->map_to_local((E.key + Vector2i(1, 1)) * get_effective_quadrant_size()));
+ r.expand_to(tile_map_node->map_to_local((E.key + Vector2i(0, 1)) * get_effective_quadrant_size()));
+ if (first) {
+ r_total = r;
+ first = false;
+ } else {
+ r_total = r_total.merge(r);
+ }
+ }
+
+ r_changed = rect_cache != r_total;
+
+ rect_cache = r_total;
+ rect_cache_dirty = false;
+ }
+#endif
+ return rect_cache;
+}
+
+HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
if (!tile_set.is_valid()) {
return HashMap<Vector2i, TileSet::TerrainsPattern>();
}
@@ -2540,9 +1159,9 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(in
for (int i = 0; i < p_to_replace.size(); i++) {
const Vector2i &coords = p_to_replace[i];
- // Select the best pattern for the given constraints
+ // Select the best pattern for the given constraints.
TileSet::TerrainsPattern current_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set);
- TileMapCell cell = get_cell(p_layer, coords);
+ TileMapCell cell = get_cell(coords);
if (cell.source_id != TileSet::INVALID_SOURCE) {
TileSetSource *source = *tile_set->get_source(cell.source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
@@ -2556,7 +1175,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(in
}
TileSet::TerrainsPattern pattern = _get_best_terrain_pattern_for_constraints(p_terrain_set, coords, constraints, current_pattern);
- // Update the constraint set with the new ones
+ // Update the constraint set with the new ones.
RBSet<TerrainConstraint> new_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, pattern);
for (const TerrainConstraint &E_constraint : new_constraints) {
if (constraints.has(E_constraint)) {
@@ -2572,12 +1191,13 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(in
return output;
}
-HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
+HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
HashMap<Vector2i, TileSet::TerrainsPattern> output;
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
- // Build list and set of tiles that can be modified (painted and their surroundings)
+ // Build list and set of tiles that can be modified (painted and their surroundings).
Vector<Vector2i> can_modify_list;
RBSet<Vector2i> can_modify_set;
RBSet<Vector2i> painted_set;
@@ -2588,11 +1208,11 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_
painted_set.insert(coords);
}
for (Vector2i coords : p_coords_array) {
- // Find the adequate neighbor
+ // Find the adequate neighbor.
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- if (is_existing_neighbor(bit)) {
- Vector2i neighbor = get_neighbor_cell(coords, bit);
+ if (tile_map_node->is_existing_neighbor(bit)) {
+ Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit);
if (!can_modify_set.has(neighbor)) {
can_modify_list.push_back(neighbor);
can_modify_set.insert(neighbor);
@@ -2601,16 +1221,16 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_
}
}
- // Build a set, out of the possibly modified tiles, of the one with a center bit that is set (or will be) to the painted terrain
+ // Build a set, out of the possibly modified tiles, of the one with a center bit that is set (or will be) to the painted terrain.
RBSet<Vector2i> cells_with_terrain_center_bit;
for (Vector2i coords : can_modify_set) {
bool connect = false;
if (painted_set.has(coords)) {
connect = true;
} else {
- // Get the center bit of the cell
+ // Get the center bit of the cell.
TileData *tile_data = nullptr;
- TileMapCell cell = get_cell(p_layer, coords);
+ TileMapCell cell = get_cell(coords);
if (cell.source_id != TileSet::INVALID_SOURCE) {
Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
Ref<TileSetAtlasSource> atlas_source = source;
@@ -2633,7 +1253,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_
// Add new constraints from the path drawn.
for (Vector2i coords : p_coords_array) {
// Constraints on the center bit.
- TerrainConstraint c = TerrainConstraint(this, coords, p_terrain);
+ TerrainConstraint c = TerrainConstraint(tile_map_node, coords, p_terrain);
c.set_priority(10);
constraints.insert(c);
@@ -2641,16 +1261,16 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
- c = TerrainConstraint(this, coords, bit, p_terrain);
+ c = TerrainConstraint(tile_map_node, coords, bit, p_terrain);
c.set_priority(10);
if ((int(bit) % 2) == 0) {
- // Side peering bits: add the constraint if the center is of the same terrain
- Vector2i neighbor = get_neighbor_cell(coords, bit);
+ // Side peering bits: add the constraint if the center is of the same terrain.
+ Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit);
if (cells_with_terrain_center_bit.has(neighbor)) {
constraints.insert(c);
}
} else {
- // Corner peering bits: add the constraint if all tiles on the constraint has the same center bit
+ // Corner peering bits: add the constraint if all tiles on the constraint has the same center bit.
HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
bool valid = true;
for (KeyValue<Vector2i, TileSet::CellNeighbor> kv : overlapping_terrain_bits) {
@@ -2668,54 +1288,55 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_
}
// Fills in the constraint list from existing tiles.
- for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) {
+ for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) {
constraints.insert(c);
}
// Fill the terrains.
- output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints);
+ output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints);
return output;
}
-HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_layer, const Vector<Vector2i> &p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
+HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
HashMap<Vector2i, TileSet::TerrainsPattern> output;
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
// Make sure the path is correct and build the peering bit list while doing it.
Vector<TileSet::CellNeighbor> neighbor_list;
- for (int i = 0; i < p_path.size() - 1; i++) {
- // Find the adequate neighbor
+ for (int i = 0; i < p_coords_array.size() - 1; i++) {
+ // Find the adequate neighbor.
TileSet::CellNeighbor found_bit = TileSet::CELL_NEIGHBOR_MAX;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- if (is_existing_neighbor(bit)) {
- if (get_neighbor_cell(p_path[i], bit) == p_path[i + 1]) {
+ if (tile_map_node->is_existing_neighbor(bit)) {
+ if (tile_map_node->get_neighbor_cell(p_coords_array[i], bit) == p_coords_array[i + 1]) {
found_bit = bit;
break;
}
}
}
- ERR_FAIL_COND_V_MSG(found_bit == TileSet::CELL_NEIGHBOR_MAX, output, vformat("Invalid terrain path, %s is not a neighboring tile of %s", p_path[i + 1], p_path[i]));
+ ERR_FAIL_COND_V_MSG(found_bit == TileSet::CELL_NEIGHBOR_MAX, output, vformat("Invalid terrain path, %s is not a neighboring tile of %s", p_coords_array[i + 1], p_coords_array[i]));
neighbor_list.push_back(found_bit);
}
- // Build list and set of tiles that can be modified (painted and their surroundings)
+ // Build list and set of tiles that can be modified (painted and their surroundings).
Vector<Vector2i> can_modify_list;
RBSet<Vector2i> can_modify_set;
RBSet<Vector2i> painted_set;
- for (int i = p_path.size() - 1; i >= 0; i--) {
- const Vector2i &coords = p_path[i];
+ for (int i = p_coords_array.size() - 1; i >= 0; i--) {
+ const Vector2i &coords = p_coords_array[i];
can_modify_list.push_back(coords);
can_modify_set.insert(coords);
painted_set.insert(coords);
}
- for (Vector2i coords : p_path) {
- // Find the adequate neighbor
+ for (Vector2i coords : p_coords_array) {
+ // Find the adequate neighbor.
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
- Vector2i neighbor = get_neighbor_cell(coords, bit);
+ Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit);
if (!can_modify_set.has(neighbor)) {
can_modify_list.push_back(neighbor);
can_modify_set.insert(neighbor);
@@ -2727,31 +1348,32 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_lay
RBSet<TerrainConstraint> constraints;
// Add new constraints from the path drawn.
- for (Vector2i coords : p_path) {
- // Constraints on the center bit
- TerrainConstraint c = TerrainConstraint(this, coords, p_terrain);
+ for (Vector2i coords : p_coords_array) {
+ // Constraints on the center bit.
+ TerrainConstraint c = TerrainConstraint(tile_map_node, coords, p_terrain);
c.set_priority(10);
constraints.insert(c);
}
- for (int i = 0; i < p_path.size() - 1; i++) {
+ for (int i = 0; i < p_coords_array.size() - 1; i++) {
// Constraints on the peering bits.
- TerrainConstraint c = TerrainConstraint(this, p_path[i], neighbor_list[i], p_terrain);
+ TerrainConstraint c = TerrainConstraint(tile_map_node, p_coords_array[i], neighbor_list[i], p_terrain);
c.set_priority(10);
constraints.insert(c);
}
// Fills in the constraint list from existing tiles.
- for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) {
+ for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) {
constraints.insert(c);
}
// Fill the terrains.
- output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints);
+ output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints);
return output;
}
-HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) {
+HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) {
HashMap<Vector2i, TileSet::TerrainsPattern> output;
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output);
@@ -2766,11 +1388,11 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_
painted_set.insert(coords);
}
for (Vector2i coords : p_coords_array) {
- // Find the adequate neighbor
+ // Find the adequate neighbor.
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
- Vector2i neighbor = get_neighbor_cell(coords, bit);
+ Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit);
if (!can_modify_set.has(neighbor)) {
can_modify_list.push_back(neighbor);
can_modify_set.insert(neighbor);
@@ -2784,7 +1406,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_
// Add new constraints from the path drawn.
for (Vector2i coords : p_coords_array) {
- // Constraints on the center bit
+ // Constraints on the center bit.
RBSet<TerrainConstraint> added_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, p_terrains_pattern);
for (TerrainConstraint c : added_constraints) {
c.set_priority(10);
@@ -2793,18 +1415,626 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_
}
// Fills in the constraint list from modified tiles border.
- for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) {
+ for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) {
constraints.insert(c);
}
// Fill the terrains.
- output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints);
+ output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints);
return output;
}
-void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
+TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords, bool p_use_proxies) const {
+ if (!tile_map.has(p_coords)) {
+ return TileMapCell();
+ } else {
+ TileMapCell c = tile_map.find(p_coords)->value;
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ if (p_use_proxies && tile_set.is_valid()) {
+ Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile);
+ c.source_id = proxyed[0];
+ c.set_atlas_coords(proxyed[1]);
+ c.alternative_tile = proxyed[2];
+ }
+ return c;
+ }
+}
+
+int TileMapLayer::get_effective_quadrant_size() const {
+ // When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant.
+ if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) {
+ return 1;
+ } else {
+ return tile_map_node->get_quadrant_size();
+ }
+}
+
+void TileMapLayer::set_tile_data(TileMapLayer::DataFormat p_format, const Vector<int> &p_data) {
+ ERR_FAIL_COND(p_format > TileMapLayer::FORMAT_3);
+
+ // Set data for a given tile from raw data.
+
+ int c = p_data.size();
+ const int *r = p_data.ptr();
+
+ int offset = (p_format >= TileMapLayer::FORMAT_2) ? 3 : 2;
+ ERR_FAIL_COND_MSG(c % offset != 0, vformat("Corrupted tile data. Got size: %s. Expected modulo: %s", offset));
+
+ clear();
+
+#ifdef DISABLE_DEPRECATED
+ ERR_FAIL_COND_MSG(p_format != TileMapLayer::FORMAT_3, vformat("Cannot handle deprecated TileMap data format version %d. This Godot version was compiled with no support for deprecated data.", p_format));
+#endif
+
+ for (int i = 0; i < c; i += offset) {
+ const uint8_t *ptr = (const uint8_t *)&r[i];
+ uint8_t local[12];
+ for (int j = 0; j < ((p_format >= TileMapLayer::FORMAT_2) ? 12 : 8); j++) {
+ local[j] = ptr[j];
+ }
+
+#ifdef BIG_ENDIAN_ENABLED
+
+ SWAP(local[0], local[3]);
+ SWAP(local[1], local[2]);
+ SWAP(local[4], local[7]);
+ SWAP(local[5], local[6]);
+ //TODO: ask someone to check this...
+ if (FORMAT >= FORMAT_2) {
+ SWAP(local[8], local[11]);
+ SWAP(local[9], local[10]);
+ }
+#endif
+ // Extracts position in TileMap.
+ int16_t x = decode_uint16(&local[0]);
+ int16_t y = decode_uint16(&local[2]);
+
+ if (p_format == TileMapLayer::FORMAT_3) {
+ uint16_t source_id = decode_uint16(&local[4]);
+ uint16_t atlas_coords_x = decode_uint16(&local[6]);
+ uint16_t atlas_coords_y = decode_uint16(&local[8]);
+ uint16_t alternative_tile = decode_uint16(&local[10]);
+ set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile);
+ } else {
+#ifndef DISABLE_DEPRECATED
+ // Previous decated format.
+
+ uint32_t v = decode_uint32(&local[4]);
+ // Extract the transform flags that used to be in the tilemap.
+ bool flip_h = v & (1UL << 29);
+ bool flip_v = v & (1UL << 30);
+ bool transpose = v & (1UL << 31);
+ v &= (1UL << 29) - 1;
+
+ // Extract autotile/atlas coords.
+ int16_t coord_x = 0;
+ int16_t coord_y = 0;
+ if (p_format == TileMapLayer::FORMAT_2) {
+ coord_x = decode_uint16(&local[8]);
+ coord_y = decode_uint16(&local[10]);
+ }
+
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ if (tile_set.is_valid()) {
+ Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose);
+ if (a.size() == 3) {
+ set_cell(Vector2i(x, y), a[0], a[1], a[2]);
+ } else {
+ ERR_PRINT(vformat("No valid tile in Tileset for: tile:%s coords:%s flip_h:%s flip_v:%s transpose:%s", v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose));
+ }
+ } else {
+ int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2);
+ set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile);
+ }
+#endif
+ }
+ }
+}
+
+Vector<int> TileMapLayer::get_tile_data() const {
+ // Export tile data to raw format.
+ Vector<int> tile_data;
+ tile_data.resize(tile_map.size() * 3);
+ int *w = tile_data.ptrw();
+
+ // Save in highest format.
+
+ int idx = 0;
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ uint8_t *ptr = (uint8_t *)&w[idx];
+ encode_uint16((int16_t)(E.key.x), &ptr[0]);
+ encode_uint16((int16_t)(E.key.y), &ptr[2]);
+ encode_uint16(E.value.source_id, &ptr[4]);
+ encode_uint16(E.value.coord_x, &ptr[6]);
+ encode_uint16(E.value.coord_y, &ptr[8]);
+ encode_uint16(E.value.alternative_tile, &ptr[10]);
+ idx += 3;
+ }
+
+ return tile_data;
+}
+
+void TileMapLayer::clear_instantiated_scenes() {
+ instantiated_scenes.clear();
+}
+
+void TileMapLayer::clear_internals() {
+ // Clear quadrants.
+ while (quadrant_map.size()) {
+ _erase_quadrant(quadrant_map.begin());
+ }
+
+ // Clear the layers internals.
+ _rendering_cleanup();
+
+ // Clear the layers internal navigation maps.
+ _navigation_cleanup();
+
+ // Clear the dirty quadrants list.
+ while (dirty_quadrant_list.first()) {
+ dirty_quadrant_list.remove(dirty_quadrant_list.first());
+ }
+}
+
+void TileMapLayer::recreate_internals() {
+ // Make sure that _clear_internals() was called prior.
+ ERR_FAIL_COND_MSG(quadrant_map.size() > 0, "TileMap layer had a non-empty quadrant map.");
+
+ if (!enabled) {
+ return;
+ }
+
+ // Update the layer internals.
+ _rendering_update();
+
+ // Update the layer internal navigation maps.
+ _navigation_update();
+
+ // Recreate the quadrants.
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ Vector2i qk = _coords_to_quadrant_coords(Vector2i(E.key.x, E.key.y));
+
+ HashMap<Vector2i, TileMapQuadrant>::Iterator Q = quadrant_map.find(qk);
+ if (!Q) {
+ Q = _create_quadrant(qk);
+ dirty_quadrant_list.add(&Q->value.dirty_list_element);
+ }
+
+ Vector2i pk = E.key;
+ Q->value.cells.insert(pk);
+
+ _make_quadrant_dirty(Q);
+ }
+
+ tile_map_node->queue_update_dirty_quadrants();
+}
+
+void TileMapLayer::notify_canvas_entered() {
+ // Rendering.
+ bool node_visible = tile_map_node->is_visible_in_tree();
+ for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : quadrant_map) {
+ TileMapQuadrant &q = E_quadrant.value;
+ for (const KeyValue<Vector2i, RID> &kv : q.occluders) {
+ Transform2D xform;
+ xform.set_origin(tile_map_node->map_to_local(kv.key));
+ RS::get_singleton()->canvas_light_occluder_attach_to_canvas(kv.value, tile_map_node->get_canvas());
+ RS::get_singleton()->canvas_light_occluder_set_transform(kv.value, tile_map_node->get_global_transform() * xform);
+ RS::get_singleton()->canvas_light_occluder_set_enabled(kv.value, node_visible);
+ }
+ }
+}
+
+void TileMapLayer::notify_visibility_changed() {
+ bool node_visible = tile_map_node->is_visible_in_tree();
+ for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : quadrant_map) {
+ TileMapQuadrant &q = E_quadrant.value;
+
+ // Update occluders transform.
+ for (const KeyValue<Vector2, Vector2i> &E_cell : q.local_to_map) {
+ Transform2D xform;
+ xform.set_origin(E_cell.key);
+ for (const KeyValue<Vector2i, RID> &kv : q.occluders) {
+ RS::get_singleton()->canvas_light_occluder_set_enabled(kv.value, node_visible);
+ }
+ }
+ }
+}
+
+void TileMapLayer::notify_xform_changed() {
+ if (!tile_map_node->is_inside_tree()) {
+ return;
+ }
+
+ bool in_editor = false;
+#ifdef TOOLS_ENABLED
+ in_editor = Engine::get_singleton()->is_editor_hint();
+#endif
+
+ Transform2D tilemap_xform = tile_map_node->get_global_transform();
+ for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : quadrant_map) {
+ TileMapQuadrant &q = E_quadrant.value;
+
+ // Update occluders transform.
+ for (const KeyValue<Vector2i, RID> &kv : q.occluders) {
+ Transform2D xform;
+ xform.set_origin(tile_map_node->map_to_local(kv.key));
+ RenderingServer::get_singleton()->canvas_light_occluder_set_transform(kv.value, tilemap_xform * xform);
+ }
+
+ // Update navigation regions transform.
+ for (const KeyValue<Vector2i, Vector<RID>> &E_region : q.navigation_regions) {
+ for (const RID &region : E_region.value) {
+ if (!region.is_valid()) {
+ continue;
+ }
+ Transform2D tile_transform;
+ tile_transform.set_origin(tile_map_node->map_to_local(E_region.key));
+ NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
+ }
+ }
+
+ // Physics.
+ if (!tile_map_node->is_collision_animatable() || in_editor) {
+ for (RID body : q.bodies) {
+ Transform2D xform;
+ xform.set_origin(tile_map_node->map_to_local(bodies_coords[body]));
+ xform = tilemap_xform * xform;
+ PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ }
+ }
+ }
+}
+
+void TileMapLayer::notify_local_xform_changed() {
+ if (!tile_map_node->is_inside_tree()) {
+ return;
+ }
+
+ bool in_editor = false;
+#ifdef TOOLS_ENABLED
+ in_editor = Engine::get_singleton()->is_editor_hint();
+#endif
+ if (!tile_map_node->is_collision_animatable() || in_editor) {
+ Transform2D gl_transform = tile_map_node->get_global_transform();
+ for (KeyValue<Vector2i, TileMapQuadrant> &E : quadrant_map) {
+ TileMapQuadrant &q = E.value;
+
+ for (RID body : q.bodies) {
+ Transform2D xform;
+ xform.set_origin(tile_map_node->map_to_local(bodies_coords[body]));
+ xform = gl_transform * xform;
+ PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ }
+ }
+ }
+}
+
+void TileMapLayer::notify_canvas_exited() {
+ for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : quadrant_map) {
+ TileMapQuadrant &q = E_quadrant.value;
+ for (const KeyValue<Vector2i, RID> &kv : q.occluders) {
+ RS::get_singleton()->canvas_light_occluder_attach_to_canvas(kv.value, RID());
+ }
+ }
+}
+
+void TileMapLayer::notify_selected_layer_changed() {
+ _rendering_update();
+}
+
+void TileMapLayer::notify_light_mask_changed() {
+ for (const KeyValue<Vector2i, TileMapQuadrant> &E : quadrant_map) {
+ for (const RID &ci : E.value.canvas_items) {
+ RenderingServer::get_singleton()->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask());
+ }
+ }
+ _rendering_update();
+}
+
+void TileMapLayer::notify_material_changed() {
+ for (KeyValue<Vector2i, TileMapQuadrant> &E : quadrant_map) {
+ TileMapQuadrant &q = E.value;
+ for (const RID &ci : q.canvas_items) {
+ RS::get_singleton()->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid());
+ }
+ }
+ _rendering_update();
+}
+
+void TileMapLayer::notify_use_parent_material_changed() {
+ notify_material_changed();
+}
+
+void TileMapLayer::notify_texture_filter_changed() {
+ for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = quadrant_map.begin(); F; ++F) {
+ TileMapQuadrant &q = F->value;
+ for (const RID &ci : q.canvas_items) {
+ RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree()));
+ _make_quadrant_dirty(F);
+ }
+ }
+ _rendering_update();
+}
+
+void TileMapLayer::notify_texture_repeat_changed() {
+ for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = quadrant_map.begin(); F; ++F) {
+ TileMapQuadrant &q = F->value;
+ for (const RID &ci : q.canvas_items) {
+ RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree()));
+ _make_quadrant_dirty(F);
+ }
+ }
+ _rendering_update();
+}
+
+void TileMapLayer::update_dirty_quadrants() {
+ // Update the coords cache.
+ for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
+ q->self()->map_to_local.clear();
+ q->self()->local_to_map.clear();
+ for (const Vector2i &E : q->self()->cells) {
+ Vector2i pk = E;
+ Vector2 pk_local_coords = tile_map_node->map_to_local(pk);
+ q->self()->map_to_local[pk] = pk_local_coords;
+ q->self()->local_to_map[pk_local_coords] = pk;
+ }
+ }
+
+ // Find TileData that need a runtime modification.
+ _build_runtime_update_tile_data(dirty_quadrant_list);
+
+ // Call the update_dirty_quadrant method on plugins.
+ _rendering_update_dirty_quadrants(dirty_quadrant_list);
+ _physics_update_dirty_quadrants(dirty_quadrant_list);
+ _navigation_update_dirty_quadrants(dirty_quadrant_list);
+ _scenes_update_dirty_quadrants(dirty_quadrant_list);
+
+ // Redraw the debug canvas_items.
+ RenderingServer *rs = RenderingServer::get_singleton();
+ for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
+ rs->canvas_item_clear(q->self()->debug_canvas_item);
+ Transform2D xform;
+ xform.set_origin(tile_map_node->map_to_local(q->self()->coords * get_effective_quadrant_size()));
+ rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform);
+
+ _rendering_draw_quadrant_debug(q->self());
+ _physics_draw_quadrant_debug(q->self());
+ _navigation_draw_quadrant_debug(q->self());
+ _scenes_draw_quadrant_debug(q->self());
+ }
+
+ // Clear the list.
+ while (dirty_quadrant_list.first()) {
+ // Clear the runtime tile data.
+ for (const KeyValue<Vector2i, TileData *> &kv : dirty_quadrant_list.first()->self()->runtime_tile_data_cache) {
+ memdelete(kv.value);
+ }
+
+ dirty_quadrant_list.remove(dirty_quadrant_list.first());
+ }
+}
+
+void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) {
+ // Set the current cell tile (using integer position).
+ Vector2i pk(p_coords);
+ HashMap<Vector2i, TileMapCell>::Iterator E = tile_map.find(pk);
+
+ int source_id = p_source_id;
+ Vector2i atlas_coords = p_atlas_coords;
+ int alternative_tile = p_alternative_tile;
+
+ if ((source_id == TileSet::INVALID_SOURCE || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) &&
+ (source_id != TileSet::INVALID_SOURCE || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) {
+ source_id = TileSet::INVALID_SOURCE;
+ atlas_coords = TileSetSource::INVALID_ATLAS_COORDS;
+ alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
+ }
+
+ if (!E && source_id == TileSet::INVALID_SOURCE) {
+ return; // Nothing to do, the tile is already empty.
+ }
+
+ // Get the quadrant
+ Vector2i qk = _coords_to_quadrant_coords(pk);
+
+ HashMap<Vector2i, TileMapQuadrant>::Iterator Q = quadrant_map.find(qk);
+
+ if (source_id == TileSet::INVALID_SOURCE) {
+ // Erase existing cell in the tile map.
+ tile_map.erase(pk);
+
+ // Erase existing cell in the quadrant.
+ ERR_FAIL_COND(!Q);
+ TileMapQuadrant &q = Q->value;
+
+ q.cells.erase(pk);
+
+ // Remove or make the quadrant dirty.
+ if (q.cells.size() == 0) {
+ _erase_quadrant(Q);
+ } else {
+ _make_quadrant_dirty(Q);
+ }
+
+ used_rect_cache_dirty = true;
+ } else {
+ if (!E) {
+ // Insert a new cell in the tile map.
+ E = tile_map.insert(pk, TileMapCell());
+
+ // Create a new quadrant if needed, then insert the cell if needed.
+ if (!Q) {
+ Q = _create_quadrant(qk);
+ }
+ TileMapQuadrant &q = Q->value;
+ q.cells.insert(pk);
+
+ } else {
+ ERR_FAIL_COND(!Q); // TileMapQuadrant should exist...
+
+ if (E->value.source_id == source_id && E->value.get_atlas_coords() == atlas_coords && E->value.alternative_tile == alternative_tile) {
+ return; // Nothing changed.
+ }
+ }
+
+ TileMapCell &c = E->value;
+
+ c.source_id = source_id;
+ c.set_atlas_coords(atlas_coords);
+ c.alternative_tile = alternative_tile;
+
+ _make_quadrant_dirty(Q);
+ used_rect_cache_dirty = true;
+ }
+}
+
+void TileMapLayer::erase_cell(const Vector2i &p_coords) {
+ set_cell(p_coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+}
+
+int TileMapLayer::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) const {
+ // Get a cell source id from position.
+ HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords);
+
+ if (!E) {
+ return TileSet::INVALID_SOURCE;
+ }
+
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ if (p_use_proxies && tile_set.is_valid()) {
+ Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile);
+ return proxyed[0];
+ }
+
+ return E->value.source_id;
+}
+
+Vector2i TileMapLayer::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies) const {
+ // Get a cell source id from position.
+ HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords);
+
+ if (!E) {
+ return TileSetSource::INVALID_ATLAS_COORDS;
+ }
+
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ if (p_use_proxies && tile_set.is_valid()) {
+ Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile);
+ return proxyed[1];
+ }
+
+ return E->value.get_atlas_coords();
+}
+
+int TileMapLayer::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies) const {
+ // Get a cell source id from position.
+ HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords);
+
+ if (!E) {
+ return TileSetSource::INVALID_TILE_ALTERNATIVE;
+ }
+
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ if (p_use_proxies && tile_set.is_valid()) {
+ Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile);
+ return proxyed[2];
+ }
+
+ return E->value.alternative_tile;
+}
+
+TileData *TileMapLayer::get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies) const {
+ int source_id = get_cell_source_id(p_coords, p_use_proxies);
+ if (source_id == TileSet::INVALID_SOURCE) {
+ return nullptr;
+ }
+
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ Ref<TileSetAtlasSource> source = tile_set->get_source(source_id);
+ if (source.is_valid()) {
+ return source->get_tile_data(get_cell_atlas_coords(p_coords, p_use_proxies), get_cell_alternative_tile(p_coords, p_use_proxies));
+ }
+
+ return nullptr;
+}
+
+void TileMapLayer::clear() {
+ // Remove all tiles.
+ clear_internals();
+ tile_map.clear();
+ recreate_internals();
+ used_rect_cache_dirty = true;
+}
+
+Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr);
+
+ Ref<TileMapPattern> output;
+ output.instantiate();
+ if (p_coords_array.is_empty()) {
+ return output;
+ }
+
+ Vector2i min = Vector2i(p_coords_array[0]);
+ for (int i = 1; i < p_coords_array.size(); i++) {
+ min = min.min(p_coords_array[i]);
+ }
+
+ Vector<Vector2i> coords_in_pattern_array;
+ coords_in_pattern_array.resize(p_coords_array.size());
+ Vector2i ensure_positive_offset;
+ for (int i = 0; i < p_coords_array.size(); i++) {
+ Vector2i coords = p_coords_array[i];
+ Vector2i coords_in_pattern = coords - min;
+ if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) {
+ if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) {
+ if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) {
+ coords_in_pattern.x -= 1;
+ if (coords_in_pattern.x < 0) {
+ ensure_positive_offset.x = 1;
+ }
+ } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) {
+ coords_in_pattern.y -= 1;
+ if (coords_in_pattern.y < 0) {
+ ensure_positive_offset.y = 1;
+ }
+ }
+ } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) {
+ if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) {
+ coords_in_pattern.x += 1;
+ } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) {
+ coords_in_pattern.y += 1;
+ }
+ }
+ }
+ coords_in_pattern_array.write[i] = coords_in_pattern;
+ }
+
+ for (int i = 0; i < coords_in_pattern_array.size(); i++) {
+ Vector2i coords = p_coords_array[i];
+ Vector2i coords_in_pattern = coords_in_pattern_array[i];
+ output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(coords), get_cell_atlas_coords(coords), get_cell_alternative_tile(coords));
+ }
+
+ return output;
+}
+
+void TileMapLayer::set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
+ ERR_FAIL_COND(tile_set.is_null());
+ ERR_FAIL_COND(p_pattern.is_null());
+
+ TypedArray<Vector2i> used_cells = p_pattern->get_used_cells();
+ for (int i = 0; i < used_cells.size(); i++) {
+ Vector2i coords = tile_map_node->map_pattern(p_position, used_cells[i], p_pattern);
+ set_cell(coords, p_pattern->get_cell_source_id(used_cells[i]), p_pattern->get_cell_atlas_coords(used_cells[i]), p_pattern->get_cell_alternative_tile(used_cells[i]));
+ }
+}
+
+void TileMapLayer::set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
Vector<Vector2i> cells_vector;
@@ -2813,16 +2043,16 @@ void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cell
cells_vector.push_back(p_cells[i]);
painted_set.insert(p_cells[i]);
}
- HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_connect(p_layer, cells_vector, p_terrain_set, p_terrain, p_ignore_empty_terrains);
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_connect(cells_vector, p_terrain_set, p_terrain, p_ignore_empty_terrains);
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) {
if (painted_set.has(kv.key)) {
// Paint a random tile with the correct terrain for the painted path.
TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
- set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile);
+ set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile);
} else {
// Avoids updating the painted path from the output if the new pattern is the same as before.
TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set);
- TileMapCell cell = get_cell(p_layer, kv.key);
+ TileMapCell cell = get_cell(kv.key);
if (cell.source_id != TileSet::INVALID_SOURCE) {
TileSetSource *source = *tile_set->get_source(cell.source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
@@ -2836,15 +2066,15 @@ void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cell
}
if (in_map_terrain_pattern != kv.value) {
TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
- set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile);
+ set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile);
}
}
}
}
-void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
+void TileMapLayer::set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
+ const Ref<TileSet> &tile_set = tile_map_node->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
Vector<Vector2i> vector_path;
@@ -2854,16 +2084,16 @@ void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, i
painted_set.insert(p_path[i]);
}
- HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_path(p_layer, vector_path, p_terrain_set, p_terrain, p_ignore_empty_terrains);
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_path(vector_path, p_terrain_set, p_terrain, p_ignore_empty_terrains);
for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) {
if (painted_set.has(kv.key)) {
// Paint a random tile with the correct terrain for the painted path.
TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
- set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile);
+ set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile);
} else {
// Avoids updating the painted path from the output if the new pattern is the same as before.
TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set);
- TileMapCell cell = get_cell(p_layer, kv.key);
+ TileMapCell cell = get_cell(kv.key);
if (cell.source_id != TileSet::INVALID_SOURCE) {
TileSetSource *source = *tile_set->get_source(cell.source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
@@ -2877,246 +2107,1233 @@ void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, i
}
if (in_map_terrain_pattern != kv.value) {
TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
- set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile);
+ set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile);
}
}
}
}
-TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileMapCell());
- const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
- if (!tile_map.has(p_coords)) {
- return TileMapCell();
- } else {
- TileMapCell c = tile_map.find(p_coords)->value;
- if (p_use_proxies && tile_set.is_valid()) {
- Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile);
- c.source_id = proxyed[0];
- c.set_atlas_coords(proxyed[1]);
- c.alternative_tile = proxyed[2];
- }
- return c;
+TypedArray<Vector2i> TileMapLayer::get_used_cells() const {
+ // Returns the cells used in the tilemap.
+ TypedArray<Vector2i> a;
+ a.resize(tile_map.size());
+ int i = 0;
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ Vector2i p(E.key.x, E.key.y);
+ a[i++] = p;
}
-}
-
-HashMap<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr);
- return &layers[p_layer].quadrant_map;
+ return a;
}
-Vector2i TileMap::get_coords_for_body_rid(RID p_physics_body) {
- ERR_FAIL_COND_V_MSG(!bodies_coords.has(p_physics_body), Vector2i(), vformat("No tiles for the given body RID %d.", p_physics_body));
- return bodies_coords[p_physics_body];
-}
+TypedArray<Vector2i> TileMapLayer::get_used_cells_by_id(int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) const {
+ // Returns the cells used in the tilemap.
+ TypedArray<Vector2i> a;
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ if ((p_source_id == TileSet::INVALID_SOURCE || p_source_id == E.value.source_id) &&
+ (p_atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || p_atlas_coords == E.value.get_atlas_coords()) &&
+ (p_alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE || p_alternative_tile == E.value.alternative_tile)) {
+ a.push_back(E.key);
+ }
+ }
-int TileMap::get_layer_for_body_rid(RID p_physics_body) {
- ERR_FAIL_COND_V_MSG(!bodies_layers.has(p_physics_body), int(), vformat("No tiles for the given body RID %d.", p_physics_body));
- return bodies_layers[p_physics_body];
+ return a;
}
-void TileMap::fix_invalid_tiles() {
- ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open.");
+Rect2i TileMapLayer::get_used_rect() const {
+ // Return the rect of the currently used area.
+ if (used_rect_cache_dirty) {
+ bool first = true;
+ used_rect_cache = Rect2i();
- for (unsigned int i = 0; i < layers.size(); i++) {
- const HashMap<Vector2i, TileMapCell> &tile_map = layers[i].tile_map;
- RBSet<Vector2i> coords;
- for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
- TileSetSource *source = *tile_set->get_source(E.value.source_id);
- if (!source || !source->has_tile(E.value.get_atlas_coords()) || !source->has_alternative_tile(E.value.get_atlas_coords(), E.value.alternative_tile)) {
- coords.insert(E.key);
+ if (tile_map.size() > 0) {
+ if (first) {
+ used_rect_cache = Rect2i(tile_map.begin()->key.x, tile_map.begin()->key.y, 0, 0);
+ first = false;
+ }
+
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ used_rect_cache.expand_to(Vector2i(E.key.x, E.key.y));
}
}
- for (const Vector2i &E : coords) {
- set_cell(i, E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+
+ if (!first) { // first is true if every layer is empty.
+ used_rect_cache.size += Vector2i(1, 1); // The cache expands to top-left coordinate, so we add one full tile.
}
+ used_rect_cache_dirty = false;
}
+
+ return used_rect_cache;
}
-void TileMap::clear_layer(int p_layer) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
+void TileMapLayer::set_name(String p_name) {
+ if (name == p_name) {
+ return;
+ }
+ name = p_name;
+ tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
+}
- // Remove all tiles.
- _clear_layer_internals(p_layer);
- layers[p_layer].tile_map.clear();
- _recreate_layer_internals(p_layer);
- used_rect_cache_dirty = true;
+String TileMapLayer::get_name() const {
+ return name;
}
-void TileMap::clear() {
- // Remove all tiles.
- _clear_internals();
- for (TileMapLayer &layer : layers) {
- layer.tile_map.clear();
+void TileMapLayer::set_enabled(bool p_enabled) {
+ if (enabled == p_enabled) {
+ return;
}
- _recreate_internals();
- used_rect_cache_dirty = true;
+ enabled = p_enabled;
+ clear_internals();
+ recreate_internals();
+ tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
+
+ tile_map_node->update_configuration_warnings();
}
-void TileMap::force_update(int p_layer) {
- if (p_layer >= 0) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
- _clear_layer_internals(p_layer);
- _recreate_layer_internals(p_layer);
- } else {
- _clear_internals();
- _recreate_internals();
+bool TileMapLayer::is_enabled() const {
+ return enabled;
+}
+
+void TileMapLayer::set_modulate(Color p_modulate) {
+ if (modulate == p_modulate) {
+ return;
}
+ modulate = p_modulate;
+ _rendering_update();
+ tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
}
-void TileMap::_set_tile_data(int p_layer, const Vector<int> &p_data) {
- ERR_FAIL_INDEX(p_layer, (int)layers.size());
- ERR_FAIL_COND(format > FORMAT_3);
+Color TileMapLayer::get_modulate() const {
+ return modulate;
+}
- // Set data for a given tile from raw data.
+void TileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) {
+ if (y_sort_enabled == p_y_sort_enabled) {
+ return;
+ }
+ y_sort_enabled = p_y_sort_enabled;
+ clear_internals();
+ recreate_internals();
+ tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
- int c = p_data.size();
- const int *r = p_data.ptr();
+ tile_map_node->update_configuration_warnings();
+}
- int offset = (format >= FORMAT_2) ? 3 : 2;
- ERR_FAIL_COND_MSG(c % offset != 0, vformat("Corrupted tile data. Got size: %s. Expected modulo: %s", offset));
+bool TileMapLayer::is_y_sort_enabled() const {
+ return y_sort_enabled;
+}
- clear_layer(p_layer);
+void TileMapLayer::set_y_sort_origin(int p_y_sort_origin) {
+ if (y_sort_origin == p_y_sort_origin) {
+ return;
+ }
+ y_sort_origin = p_y_sort_origin;
+ clear_internals();
+ recreate_internals();
+ tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
+}
-#ifdef DISABLE_DEPRECATED
- ERR_FAIL_COND_MSG(format != FORMAT_3, vformat("Cannot handle deprecated TileMap data format version %d. This Godot version was compiled with no support for deprecated data.", format));
-#endif
+int TileMapLayer::get_y_sort_origin() const {
+ return y_sort_origin;
+}
- for (int i = 0; i < c; i += offset) {
- const uint8_t *ptr = (const uint8_t *)&r[i];
- uint8_t local[12];
- for (int j = 0; j < ((format >= FORMAT_2) ? 12 : 8); j++) {
- local[j] = ptr[j];
+void TileMapLayer::set_z_index(int p_z_index) {
+ if (z_index == p_z_index) {
+ return;
+ }
+ z_index = p_z_index;
+ _rendering_update();
+ tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed);
+
+ tile_map_node->update_configuration_warnings();
+}
+
+int TileMapLayer::get_z_index() const {
+ return z_index;
+}
+
+void TileMapLayer::set_navigation_map(RID p_map) {
+ ERR_FAIL_COND_MSG(!tile_map_node->is_inside_tree(), "A TileMap navigation map can only be changed while inside the SceneTree.");
+ navigation_map = p_map;
+ uses_world_navigation_map = p_map == tile_map_node->get_world_2d()->get_navigation_map();
+}
+
+RID TileMapLayer::get_navigation_map() const {
+ if (navigation_map.is_valid()) {
+ return navigation_map;
+ }
+ return RID();
+}
+
+void TileMapLayer::force_update() {
+ clear_internals();
+ recreate_internals();
+}
+
+void TileMapLayer::fix_invalid_tiles() {
+ RBSet<Vector2i> coords;
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ TileSetSource *source = *(tile_map_node->get_tileset())->get_source(E.value.source_id);
+ if (!source || !source->has_tile(E.value.get_atlas_coords()) || !source->has_alternative_tile(E.value.get_atlas_coords(), E.value.alternative_tile)) {
+ coords.insert(E.key);
}
+ }
+ for (const Vector2i &E : coords) {
+ set_cell(E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+ }
+}
-#ifdef BIG_ENDIAN_ENABLED
+bool TileMapLayer::has_body_rid(RID p_physics_body) const {
+ return bodies_coords.has(p_physics_body);
+}
- SWAP(local[0], local[3]);
- SWAP(local[1], local[2]);
- SWAP(local[4], local[7]);
- SWAP(local[5], local[6]);
- //TODO: ask someone to check this...
- if (FORMAT >= FORMAT_2) {
- SWAP(local[8], local[11]);
- SWAP(local[9], local[10]);
+Vector2i TileMapLayer::get_coords_for_body_rid(RID p_physics_body) const {
+ return bodies_coords[p_physics_body];
+}
+
+HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coords_and_peering_bits() const {
+ HashMap<Vector2i, TileSet::CellNeighbor> output;
+
+ ERR_FAIL_COND_V(is_center_bit(), output);
+
+ Ref<TileSet> ts = tile_map->get_tileset();
+ ERR_FAIL_COND_V(!ts.is_valid(), output);
+
+ TileSet::TileShape shape = ts->get_tile_shape();
+ if (shape == TileSet::TILE_SHAPE_SQUARE) {
+ switch (bit) {
+ case 1:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
+ break;
+ case 2:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+ break;
+ case 3:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
+ break;
+ default:
+ ERR_FAIL_V(output);
}
-#endif
- // Extracts position in TileMap.
- int16_t x = decode_uint16(&local[0]);
- int16_t y = decode_uint16(&local[2]);
+ } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ switch (bit) {
+ case 1:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+ break;
+ case 2:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
+ break;
+ case 3:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ } else {
+ // Half offset shapes.
+ TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis();
+ if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (bit) {
+ case 1:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
+ break;
+ case 2:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+ break;
+ case 3:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+ break;
+ case 4:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+ break;
+ case 5:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ } else {
+ switch (bit) {
+ case 1:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ break;
+ case 2:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+ break;
+ case 3:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ break;
+ case 4:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
+ break;
+ case 5:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ }
+ }
+ return output;
+}
- if (format == FORMAT_3) {
- uint16_t source_id = decode_uint16(&local[4]);
- uint16_t atlas_coords_x = decode_uint16(&local[6]);
- uint16_t atlas_coords_y = decode_uint16(&local[8]);
- uint16_t alternative_tile = decode_uint16(&local[10]);
- set_cell(p_layer, Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile);
+TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain) {
+ tile_map = p_tile_map;
+
+ Ref<TileSet> ts = tile_map->get_tileset();
+ ERR_FAIL_COND(!ts.is_valid());
+
+ bit = 0;
+ base_cell_coords = p_position;
+ terrain = p_terrain;
+}
+
+TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
+ // The way we build the constraint make it easy to detect conflicting constraints.
+ tile_map = p_tile_map;
+
+ Ref<TileSet> ts = tile_map->get_tileset();
+ ERR_FAIL_COND(!ts.is_valid());
+
+ TileSet::TileShape shape = ts->get_tile_shape();
+ if (shape == TileSet::TILE_SHAPE_SQUARE) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ bit = 3;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ bit = 3;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
+ } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ bit = 3;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ bit = 3;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
+ } else {
+ // Half-offset shapes.
+ TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis();
+ if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ bit = 3;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ bit = 4;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ bit = 5;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ bit = 4;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ bit = 3;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ bit = 5;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ bit = 4;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
} else {
-#ifndef DISABLE_DEPRECATED
- // Previous decated format.
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ bit = 3;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ bit = 4;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ bit = 5;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ bit = 3;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ bit = 4;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ bit = 3;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ bit = 5;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
+ }
+ }
+ terrain = p_terrain;
+}
- uint32_t v = decode_uint32(&local[4]);
- // Extract the transform flags that used to be in the tilemap.
- bool flip_h = v & (1UL << 29);
- bool flip_v = v & (1UL << 30);
- bool transpose = v & (1UL << 31);
- v &= (1UL << 29) - 1;
+#define TILEMAP_CALL_FOR_LAYER(layer, function, ...) \
+ if (layer < 0) { \
+ layer = layers.size() + layer; \
+ }; \
+ ERR_FAIL_INDEX(layer, (int)layers.size()); \
+ layers[layer]->function(__VA_ARGS__);
- // Extract autotile/atlas coords.
- int16_t coord_x = 0;
- int16_t coord_y = 0;
- if (format == FORMAT_2) {
- coord_x = decode_uint16(&local[8]);
- coord_y = decode_uint16(&local[10]);
+#define TILEMAP_CALL_FOR_LAYER_V(layer, err_value, function, ...) \
+ if (layer < 0) { \
+ layer = layers.size() + layer; \
+ }; \
+ ERR_FAIL_INDEX_V(layer, (int)layers.size(), err_value); \
+ return layers[layer]->function(__VA_ARGS__);
+
+Vector2i TileMap::transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) {
+ // Transform to stacked layout.
+ Vector2i output = p_coords;
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ SWAP(output.x, output.y);
+ }
+ switch (p_from_layout) {
+ case TileSet::TILE_LAYOUT_STACKED:
+ break;
+ case TileSet::TILE_LAYOUT_STACKED_OFFSET:
+ if (output.y % 2) {
+ output.x -= 1;
+ }
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
+ case TileSet::TILE_LAYOUT_STAIRS_DOWN:
+ if ((p_from_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
+ if (output.y < 0 && bool(output.y % 2)) {
+ output = Vector2i(output.x + output.y / 2 - 1, output.y);
+ } else {
+ output = Vector2i(output.x + output.y / 2, output.y);
+ }
+ } else {
+ if (output.x < 0 && bool(output.x % 2)) {
+ output = Vector2i(output.x / 2 - 1, output.x + output.y * 2);
+ } else {
+ output = Vector2i(output.x / 2, output.x + output.y * 2);
+ }
+ }
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
+ case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
+ if ((p_from_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
+ if ((output.x + output.y) < 0 && (output.x - output.y) % 2) {
+ output = Vector2i((output.x + output.y) / 2 - 1, output.y - output.x);
+ } else {
+ output = Vector2i((output.x + output.y) / 2, -output.x + output.y);
+ }
+ } else {
+ if ((output.x - output.y) < 0 && (output.x + output.y) % 2) {
+ output = Vector2i((output.x - output.y) / 2 - 1, output.x + output.y);
+ } else {
+ output = Vector2i((output.x - output.y) / 2, output.x + output.y);
+ }
}
+ break;
+ }
- if (tile_set.is_valid()) {
- Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose);
- if (a.size() == 3) {
- set_cell(p_layer, Vector2i(x, y), a[0], a[1], a[2]);
+ switch (p_to_layout) {
+ case TileSet::TILE_LAYOUT_STACKED:
+ break;
+ case TileSet::TILE_LAYOUT_STACKED_OFFSET:
+ if (output.y % 2) {
+ output.x += 1;
+ }
+ break;
+ case TileSet::TILE_LAYOUT_STAIRS_RIGHT:
+ case TileSet::TILE_LAYOUT_STAIRS_DOWN:
+ if ((p_to_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
+ if (output.y < 0 && (output.y % 2)) {
+ output = Vector2i(output.x - output.y / 2 + 1, output.y);
} else {
- ERR_PRINT(vformat("No valid tile in Tileset for: tile:%s coords:%s flip_h:%s flip_v:%s transpose:%s", v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose));
+ output = Vector2i(output.x - output.y / 2, output.y);
}
} else {
- int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2);
- set_cell(p_layer, Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile);
+ if (output.y % 2) {
+ if (output.y < 0) {
+ output = Vector2i(2 * output.x + 1, -output.x + output.y / 2 - 1);
+ } else {
+ output = Vector2i(2 * output.x + 1, -output.x + output.y / 2);
+ }
+ } else {
+ output = Vector2i(2 * output.x, -output.x + output.y / 2);
+ }
}
+ break;
+ case TileSet::TILE_LAYOUT_DIAMOND_RIGHT:
+ case TileSet::TILE_LAYOUT_DIAMOND_DOWN:
+ if ((p_to_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) {
+ if (output.y % 2) {
+ if (output.y > 0) {
+ output = Vector2i(output.x - output.y / 2, output.x + output.y / 2 + 1);
+ } else {
+ output = Vector2i(output.x - output.y / 2 + 1, output.x + output.y / 2);
+ }
+ } else {
+ output = Vector2i(output.x - output.y / 2, output.x + output.y / 2);
+ }
+ } else {
+ if (output.y % 2) {
+ if (output.y < 0) {
+ output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2 - 1);
+ } else {
+ output = Vector2i(output.x + output.y / 2 + 1, -output.x + output.y / 2);
+ }
+ } else {
+ output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2);
+ }
+ }
+ break;
+ }
+
+ if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) {
+ SWAP(output.x, output.y);
+ }
+
+ return output;
+}
+
+void TileMap::set_selected_layer(int p_layer_id) {
+ ERR_FAIL_COND(p_layer_id < -1 || p_layer_id >= (int)layers.size());
+ selected_layer = p_layer_id;
+ emit_signal(CoreStringNames::get_singleton()->changed);
+
+ // Update the layers modulation.
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_selected_layer_changed();
+ }
+}
+
+int TileMap::get_selected_layer() const {
+ return selected_layer;
+}
+
+void TileMap::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ _clear_internals();
+ _recreate_internals();
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ _clear_internals();
+ } break;
+ }
+
+ // Transfers the notification to tileset plugins.
+ if (tile_set.is_valid()) {
+ switch (p_what) {
+ case TileMap::NOTIFICATION_ENTER_CANVAS: {
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_canvas_entered();
+ }
+ } break;
+
+ case TileMap::NOTIFICATION_EXIT_CANVAS: {
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_canvas_exited();
+ }
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ // Rendering.
+ if (tile_set.is_valid()) {
+ RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), is_y_sort_enabled());
+ }
+ } break;
+
+ case TileMap::NOTIFICATION_VISIBILITY_CHANGED: {
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_visibility_changed();
+ }
+ } break;
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ // Physics.
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_xform_changed();
+ }
+ } break;
+
+ case TileMap::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ // Physics.
+ bool in_editor = false;
+#ifdef TOOLS_ENABLED
+ in_editor = Engine::get_singleton()->is_editor_hint();
#endif
+ if (is_inside_tree() && collision_animatable && !in_editor) {
+ // Update transform on the physics tick when in animatable mode.
+ last_valid_transform = new_transform;
+ set_notify_local_transform(false);
+ set_global_transform(new_transform);
+ set_notify_local_transform(true);
+ }
+ } break;
+
+ case TileMap::NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_local_xform_changed();
+ }
+
+ // Physics.
+ bool in_editor = false;
+#ifdef TOOLS_ENABLED
+ in_editor = Engine::get_singleton()->is_editor_hint();
+#endif
+
+ // Only active when animatable. Send the new transform to the physics...
+ if (is_inside_tree() && !in_editor && collision_animatable) {
+ // ... but then revert changes.
+ set_notify_local_transform(false);
+ set_global_transform(last_valid_transform);
+ set_notify_local_transform(true);
+ }
+ } break;
}
}
- emit_signal(SNAME("changed"));
}
-Vector<int> TileMap::_get_tile_data(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Vector<int>());
+void TileMap::queue_update_dirty_quadrants() {
+ if (pending_update || !is_inside_tree()) {
+ return;
+ }
+ pending_update = true;
+ call_deferred(SNAME("_update_dirty_quadrants"));
+}
- // Export tile data to raw format
- const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
- Vector<int> tile_data;
- tile_data.resize(tile_map.size() * 3);
- int *w = tile_data.ptrw();
+void TileMap::_update_dirty_quadrants() {
+ if (!pending_update) {
+ return;
+ }
- // Save in highest format
+ if (!is_inside_tree() || !tile_set.is_valid()) {
+ pending_update = false;
+ return;
+ }
- int idx = 0;
- for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
- uint8_t *ptr = (uint8_t *)&w[idx];
- encode_uint16((int16_t)(E.key.x), &ptr[0]);
- encode_uint16((int16_t)(E.key.y), &ptr[2]);
- encode_uint16(E.value.source_id, &ptr[4]);
- encode_uint16(E.value.coord_x, &ptr[6]);
- encode_uint16(E.value.coord_y, &ptr[8]);
- encode_uint16(E.value.alternative_tile, &ptr[10]);
- idx += 3;
+ // Physics:
+ Transform2D gl_transform = get_global_transform();
+ last_valid_transform = gl_transform;
+ new_transform = gl_transform;
+
+ // Update dirty quadrants on layers.
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->update_dirty_quadrants();
}
- return tile_data;
+ pending_update = false;
+}
+
+void TileMap::set_tileset(const Ref<TileSet> &p_tileset) {
+ if (p_tileset == tile_set) {
+ return;
+ }
+
+ // Set the tileset, registering to its changes.
+ if (tile_set.is_valid()) {
+ tile_set->disconnect_changed(callable_mp(this, &TileMap::_tile_set_changed));
+ }
+
+ if (!p_tileset.is_valid()) {
+ _clear_internals();
+ }
+
+ tile_set = p_tileset;
+
+ if (tile_set.is_valid()) {
+ tile_set->connect_changed(callable_mp(this, &TileMap::_tile_set_changed));
+ _clear_internals();
+ _recreate_internals();
+ }
+
+ emit_signal(CoreStringNames::get_singleton()->changed);
}
-void TileMap::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
- if (GDVIRTUAL_IS_OVERRIDDEN(_use_tile_data_runtime_update) && GDVIRTUAL_IS_OVERRIDDEN(_tile_data_runtime_update)) {
- SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
- while (q_list_element) {
- TileMapQuadrant &q = *q_list_element->self();
- // Iterate over the cells of the quadrant.
- for (const KeyValue<Vector2, Vector2i> &E_cell : q.local_to_map) {
- TileMapCell c = get_cell(q.layer, E_cell.value, true);
+Ref<TileSet> TileMap::get_tileset() const {
+ return tile_set;
+}
- TileSetSource *source;
- if (tile_set->has_source(c.source_id)) {
- source = *tile_set->get_source(c.source_id);
+void TileMap::set_quadrant_size(int p_size) {
+ ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
- if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
- continue;
- }
+ quadrant_size = p_size;
+ _clear_internals();
+ _recreate_internals();
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- bool ret = false;
- if (GDVIRTUAL_CALL(_use_tile_data_runtime_update, q.layer, E_cell.value, ret) && ret) {
- TileData *tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
+int TileMap::get_quadrant_size() const {
+ return quadrant_size;
+}
- // Create the runtime TileData.
- TileData *tile_data_runtime_use = tile_data->duplicate();
- tile_data_runtime_use->set_allow_transform(true);
- q.runtime_tile_data_cache[E_cell.value] = tile_data_runtime_use;
+void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_animation_offset) {
+ ERR_FAIL_COND(!p_tile_set.is_valid());
+ ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id));
+ ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords));
+ ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile));
+ TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Check for the frame.
+ if (p_frame >= 0) {
+ ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords));
+ }
- GDVIRTUAL_CALL(_tile_data_runtime_update, q.layer, E_cell.value, tile_data_runtime_use);
- }
- }
- }
+ // Get the texture.
+ Ref<Texture2D> tex = atlas_source->get_runtime_texture();
+ if (!tex.is_valid()) {
+ return;
+ }
+
+ // Check if we are in the texture, return otherwise.
+ Vector2i grid_size = atlas_source->get_atlas_grid_size();
+ if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) {
+ return;
+ }
+
+ // Get tile data.
+ const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile);
+
+ // Get the tile modulation.
+ Color modulate = tile_data->get_modulate() * p_modulation;
+
+ // Compute the offset.
+ Vector2 tile_offset = tile_data->get_texture_origin();
+
+ // Get destination rect.
+ Rect2 dest_rect;
+ dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size;
+ dest_rect.size.x += FP_ADJUST;
+ dest_rect.size.y += FP_ADJUST;
+
+ bool transpose = tile_data->get_transpose();
+ if (transpose) {
+ dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
+ } else {
+ dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset);
+ }
+
+ if (tile_data->get_flip_h()) {
+ dest_rect.size.x = -dest_rect.size.x;
+ }
+
+ if (tile_data->get_flip_v()) {
+ dest_rect.size.y = -dest_rect.size.y;
+ }
+
+ // Draw the tile.
+ if (p_frame >= 0) {
+ Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame);
+ tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
+ } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) {
+ Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0);
+ tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
+ } else {
+ real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords);
+ real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed;
+ real_t time = 0.0;
+ for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) {
+ real_t frame_duration = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame) / speed;
+ RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, time, time + frame_duration, p_animation_offset);
+
+ Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame);
+ tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping());
+
+ time += frame_duration;
}
- q_list_element = q_list_element->next();
+ RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0);
}
}
}
+int TileMap::get_layers_count() const {
+ return layers.size();
+}
+
+void TileMap::add_layer(int p_to_pos) {
+ if (p_to_pos < 0) {
+ p_to_pos = layers.size() + p_to_pos + 1;
+ }
+
+ ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
+
+ // Must clear before adding the layer.
+ _clear_internals();
+ Ref<TileMapLayer> new_layer;
+ new_layer.instantiate();
+ new_layer->set_tile_map(this);
+ layers.insert(p_to_pos, new_layer);
+ for (unsigned int i = 0; i < layers.size(); i++) {
+ layers[i]->set_layer_index_in_tile_map_node(i);
+ }
+ _recreate_internals();
+ notify_property_list_changed();
+
+ emit_signal(CoreStringNames::get_singleton()->changed);
+
+ update_configuration_warnings();
+}
+
+void TileMap::move_layer(int p_layer, int p_to_pos) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1);
+
+ // Clear before shuffling layers.
+ _clear_internals();
+ Ref<TileMapLayer> layer = layers[p_layer];
+ layers.insert(p_to_pos, layer);
+ layers.remove_at(p_to_pos < p_layer ? p_layer + 1 : p_layer);
+ for (unsigned int i = 0; i < layers.size(); i++) {
+ layers[i]->set_layer_index_in_tile_map_node(i);
+ }
+ _recreate_internals();
+ notify_property_list_changed();
+
+ if (selected_layer == p_layer) {
+ selected_layer = p_to_pos < p_layer ? p_to_pos - 1 : p_to_pos;
+ }
+
+ emit_signal(CoreStringNames::get_singleton()->changed);
+
+ update_configuration_warnings();
+}
+
+void TileMap::remove_layer(int p_layer) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+
+ // Clear before removing the layer.
+ _clear_internals();
+ layers.remove_at(p_layer);
+ for (unsigned int i = 0; i < layers.size(); i++) {
+ layers[i]->set_layer_index_in_tile_map_node(i);
+ }
+ _recreate_internals();
+ notify_property_list_changed();
+
+ if (selected_layer >= p_layer) {
+ selected_layer -= 1;
+ }
+
+ emit_signal(CoreStringNames::get_singleton()->changed);
+
+ update_configuration_warnings();
+}
+
+void TileMap::set_layer_name(int p_layer, String p_name) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_name, p_name);
+}
+
+String TileMap::get_layer_name(int p_layer) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, "", get_name);
+}
+
+void TileMap::set_layer_enabled(int p_layer, bool p_enabled) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_enabled, p_enabled);
+}
+
+bool TileMap::is_layer_enabled(int p_layer) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, false, is_enabled);
+}
+
+void TileMap::set_layer_modulate(int p_layer, Color p_modulate) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_modulate, p_modulate);
+}
+
+Color TileMap::get_layer_modulate(int p_layer) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, Color(), get_modulate);
+}
+
+void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_y_sort_enabled, p_y_sort_enabled);
+}
+
+bool TileMap::is_layer_y_sort_enabled(int p_layer) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, false, is_y_sort_enabled);
+}
+
+void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_y_sort_origin, p_y_sort_origin);
+}
+
+int TileMap::get_layer_y_sort_origin(int p_layer) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, 0, get_y_sort_origin);
+}
+
+void TileMap::set_layer_z_index(int p_layer, int p_z_index) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_z_index, p_z_index);
+}
+
+int TileMap::get_layer_z_index(int p_layer) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, 0, get_z_index);
+}
+
+void TileMap::set_layer_navigation_map(int p_layer, RID p_map) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_navigation_map, p_map);
+}
+
+RID TileMap::get_layer_navigation_map(int p_layer) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, RID(), get_navigation_map);
+}
+
+void TileMap::set_collision_animatable(bool p_enabled) {
+ if (collision_animatable == p_enabled) {
+ return;
+ }
+ collision_animatable = p_enabled;
+ _clear_internals();
+ set_notify_local_transform(p_enabled);
+ set_physics_process_internal(p_enabled);
+ _recreate_internals();
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+bool TileMap::is_collision_animatable() const {
+ return collision_animatable;
+}
+
+void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) {
+ if (collision_visibility_mode == p_show_collision) {
+ return;
+ }
+ collision_visibility_mode = p_show_collision;
+ _clear_internals();
+ _recreate_internals();
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+TileMap::VisibilityMode TileMap::get_collision_visibility_mode() {
+ return collision_visibility_mode;
+}
+
+void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navigation) {
+ if (navigation_visibility_mode == p_show_navigation) {
+ return;
+ }
+ navigation_visibility_mode = p_show_navigation;
+ _clear_internals();
+ _recreate_internals();
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() {
+ return navigation_visibility_mode;
+}
+
+void TileMap::set_y_sort_enabled(bool p_enable) {
+ if (is_y_sort_enabled() == p_enable) {
+ return;
+ }
+ Node2D::set_y_sort_enabled(p_enable);
+ _clear_internals();
+ _recreate_internals();
+ emit_signal(CoreStringNames::get_singleton()->changed);
+ update_configuration_warnings();
+}
+
+void TileMap::_clear_internals() {
+ // Clear quadrants.
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->clear_internals();
+ }
+}
+
+void TileMap::_recreate_internals() {
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->recreate_internals();
+ }
+}
+
+/////////////////////////////// Rendering //////////////////////////////////////
+
+void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_cell, p_coords, p_source_id, p_atlas_coords, p_alternative_tile);
+}
+
+void TileMap::erase_cell(int p_layer, const Vector2i &p_coords) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_cell, p_coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+}
+
+int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, TileSet::INVALID_SOURCE, get_cell_source_id, p_coords, p_use_proxies);
+}
+
+Vector2i TileMap::get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, TileSetSource::INVALID_ATLAS_COORDS, get_cell_atlas_coords, p_coords, p_use_proxies);
+}
+
+int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, TileSetSource::INVALID_TILE_ALTERNATIVE, get_cell_alternative_tile, p_coords, p_use_proxies);
+}
+
+TileData *TileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, nullptr, get_cell_tile_data, p_coords, p_use_proxies);
+}
+
+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);
+}
+
+Vector2i TileMap::map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) {
+ ERR_FAIL_COND_V(p_pattern.is_null(), Vector2i());
+ ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i());
+
+ Vector2i output = p_position_in_tilemap + p_coords_in_pattern;
+ if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) {
+ if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) {
+ if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) {
+ output.x += 1;
+ } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) {
+ output.y += 1;
+ }
+ } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) {
+ if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) {
+ output.x -= 1;
+ } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) {
+ output.y -= 1;
+ }
+ }
+ }
+
+ return output;
+}
+
+void TileMap::set_pattern(int p_layer, const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_pattern, p_position, p_pattern);
+}
+
+HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(int p_layer, const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) {
+ HashMap<Vector2i, TileSet::TerrainsPattern> err_value;
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, err_value, terrain_fill_constraints, p_to_replace, p_terrain_set, p_constraints);
+}
+
+HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
+ HashMap<Vector2i, TileSet::TerrainsPattern> err_value;
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, err_value, terrain_fill_connect, p_coords_array, p_terrain_set, p_terrain, p_ignore_empty_terrains);
+}
+
+HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
+ HashMap<Vector2i, TileSet::TerrainsPattern> err_value;
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, err_value, terrain_fill_path, p_coords_array, p_terrain_set, p_terrain, p_ignore_empty_terrains);
+}
+
+HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) {
+ HashMap<Vector2i, TileSet::TerrainsPattern> err_value;
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, err_value, terrain_fill_pattern, p_coords_array, p_terrain_set, p_terrains_pattern, p_ignore_empty_terrains);
+}
+
+void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_cells_terrain_connect, p_cells, p_terrain_set, p_terrain, p_ignore_empty_terrains);
+}
+
+void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, set_cells_terrain_path, p_path, p_terrain_set, p_terrain, p_ignore_empty_terrains);
+}
+
+TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, TileMapCell(), get_cell, p_coords, p_use_proxies);
+}
+
+Vector2i TileMap::get_coords_for_body_rid(RID p_physics_body) {
+ for (const Ref<TileMapLayer> &layer : layers) {
+ if (layer->has_body_rid(p_physics_body)) {
+ return layer->get_coords_for_body_rid(p_physics_body);
+ }
+ }
+ ERR_FAIL_V_MSG(Vector2i(), vformat("No tiles for the given body RID %d.", p_physics_body));
+}
+
+int TileMap::get_layer_for_body_rid(RID p_physics_body) {
+ for (unsigned int i = 0; i < layers.size(); i++) {
+ if (layers[i]->has_body_rid(p_physics_body)) {
+ return i;
+ }
+ }
+ ERR_FAIL_V_MSG(-1, vformat("No tiles for the given body RID %d.", p_physics_body));
+}
+
+void TileMap::fix_invalid_tiles() {
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->fix_invalid_tiles();
+ }
+}
+
+void TileMap::clear_layer(int p_layer) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, clear)
+}
+
+void TileMap::clear() {
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->clear();
+ }
+}
+
+void TileMap::force_update(int p_layer) {
+ if (p_layer >= 0) {
+ TILEMAP_CALL_FOR_LAYER(p_layer, force_update);
+ } else {
+ _clear_internals();
+ _recreate_internals();
+ }
+}
+
#ifdef TOOLS_ENABLED
Rect2 TileMap::_edit_get_rect() const {
- // Return the visible rect of the tilemap
- const_cast<TileMap *>(this)->_recompute_rect_cache();
- return rect_cache;
+ // Return the visible rect of the tilemap.
+ if (layers.is_empty()) {
+ return Rect2();
+ }
+
+ bool any_changed = false;
+ bool changed = false;
+ Rect2 rect = layers[0]->get_rect(changed);
+ any_changed |= changed;
+ for (unsigned int i = 1; i < layers.size(); i++) {
+ rect = rect.merge(layers[i]->get_rect(changed));
+ any_changed |= changed;
+ }
+ const_cast<TileMap *>(this)->item_rect_changed(any_changed);
+ return rect;
}
#endif
@@ -3124,15 +3341,20 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
Vector<String> components = String(p_name).split("/", true, 2);
if (p_name == "format") {
if (p_value.get_type() == Variant::INT) {
- format = (DataFormat)(p_value.operator int64_t()); // Set format used for loading
+ format = (TileMapLayer::DataFormat)(p_value.operator int64_t()); // Set format used for loading.
return true;
}
} else if (p_name == "tile_data") { // Kept for compatibility reasons.
if (p_value.is_array()) {
- if (layers.size() < 1) {
- layers.resize(1);
+ if (layers.size() == 0) {
+ Ref<TileMapLayer> new_layer;
+ new_layer.instantiate();
+ new_layer->set_tile_map(this);
+ new_layer->set_layer_index_in_tile_map_node(0);
+ layers.push_back(new_layer);
}
- _set_tile_data(0, p_value);
+ layers[0]->set_tile_data(format, p_value);
+ emit_signal(CoreStringNames::get_singleton()->changed);
return true;
}
return false;
@@ -3145,12 +3367,16 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
if (index >= (int)layers.size()) {
_clear_internals();
while (index >= (int)layers.size()) {
- layers.push_back(TileMapLayer());
+ Ref<TileMapLayer> new_layer;
+ new_layer.instantiate();
+ new_layer->set_tile_map(this);
+ new_layer->set_layer_index_in_tile_map_node(index);
+ layers.push_back(new_layer);
}
_recreate_internals();
notify_property_list_changed();
- emit_signal(SNAME("changed"));
+ emit_signal(CoreStringNames::get_singleton()->changed);
update_configuration_warnings();
}
@@ -3173,7 +3399,8 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
set_layer_z_index(index, p_value);
return true;
} else if (components[1] == "tile_data") {
- _set_tile_data(index, p_value);
+ layers[index]->set_tile_data(format, p_value);
+ emit_signal(CoreStringNames::get_singleton()->changed);
return true;
} else {
return false;
@@ -3185,7 +3412,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
Vector<String> components = String(p_name).split("/", true, 2);
if (p_name == "format") {
- r_ret = FORMAT_3; // When saving, always save highest format
+ r_ret = TileMapLayer::FORMAT_MAX - 1; // When saving, always save highest format.
return true;
} else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) {
int index = components[0].trim_prefix("layer_").to_int();
@@ -3212,7 +3439,7 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_layer_z_index(index);
return true;
} else if (components[1] == "tile_data") {
- r_ret = _get_tile_data(index);
+ r_ret = layers[index]->get_tile_data();
return true;
} else {
return false;
@@ -3236,7 +3463,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const {
}
Vector2 TileMap::map_to_local(const Vector2i &p_pos) const {
- // SHOULD RETURN THE CENTER OF THE CELL
+ // SHOULD RETURN THE CENTER OF THE CELL.
ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2());
Vector2 ret = p_pos;
@@ -3245,7 +3472,7 @@ Vector2 TileMap::map_to_local(const Vector2i &p_pos) const {
if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
// Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap.
- // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap
+ // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap.
if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
switch (tile_set->get_tile_layout()) {
case TileSet::TILE_LAYOUT_STACKED:
@@ -3267,7 +3494,7 @@ Vector2 TileMap::map_to_local(const Vector2i &p_pos) const {
ret = Vector2((ret.x - ret.y) / 2, ret.y + ret.x);
break;
}
- } else { // TILE_OFFSET_AXIS_VERTICAL
+ } else { // TILE_OFFSET_AXIS_VERTICAL.
switch (tile_set->get_tile_layout()) {
case TileSet::TILE_LAYOUT_STACKED:
ret = Vector2(ret.x, ret.y + (Math::posmod(ret.x, 2) == 0 ? 0.0 : 0.5));
@@ -3291,7 +3518,7 @@ Vector2 TileMap::map_to_local(const Vector2i &p_pos) const {
}
}
- // Multiply by the overlapping ratio
+ // Multiply by the overlapping ratio.
double overlapping_ratio = 1.0;
if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
@@ -3300,7 +3527,7 @@ Vector2 TileMap::map_to_local(const Vector2i &p_pos) const {
overlapping_ratio = 0.75;
}
ret.y *= overlapping_ratio;
- } else { // TILE_OFFSET_AXIS_VERTICAL
+ } else { // TILE_OFFSET_AXIS_VERTICAL.
if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
overlapping_ratio = 0.5;
} else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) {
@@ -3322,7 +3549,7 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const {
TileSet::TileOffsetAxis tile_offset_axis = tile_set->get_tile_offset_axis();
TileSet::TileLayout tile_layout = tile_set->get_tile_layout();
- // Divide by the overlapping ratio
+ // Divide by the overlapping ratio.
double overlapping_ratio = 1.0;
if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
@@ -3331,7 +3558,7 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const {
overlapping_ratio = 0.75;
}
ret.y /= overlapping_ratio;
- } else { // TILE_OFFSET_AXIS_VERTICAL
+ } else { // TILE_OFFSET_AXIS_VERTICAL.
if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
overlapping_ratio = 0.5;
} else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) {
@@ -3343,7 +3570,7 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const {
// For each half-offset shape, we check if we are in the corner of the tile, and thus should correct the local position accordingly.
if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) {
// Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap.
- // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap
+ // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap.
if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
// Smart floor of the position
Vector2 raw_pos = ret;
@@ -3353,7 +3580,7 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const {
ret = ret.floor();
}
- // Compute the tile offset, and if we might the output for a neighbor top tile
+ // Compute the tile offset, and if we might the output for a neighbor top tile.
Vector2 in_tile_pos = raw_pos - ret;
bool in_top_left_triangle = (in_tile_pos - Vector2(0.5, 0.0)).cross(Vector2(-0.5, 1.0 / overlapping_ratio - 1)) <= 0;
bool in_top_right_triangle = (in_tile_pos - Vector2(0.5, 0.0)).cross(Vector2(0.5, 1.0 / overlapping_ratio - 1)) > 0;
@@ -3408,8 +3635,8 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const {
}
break;
}
- } else { // TILE_OFFSET_AXIS_VERTICAL
- // Smart floor of the position
+ } else { // TILE_OFFSET_AXIS_VERTICAL.
+ // Smart floor of the position.
Vector2 raw_pos = ret;
if (Math::posmod(Math::floor(ret.x), 2) ^ (tile_layout == TileSet::TILE_LAYOUT_STACKED_OFFSET)) {
ret = Vector2(Math::floor(ret.x), Math::floor(ret.y + 0.5) - 0.5);
@@ -3417,7 +3644,7 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const {
ret = ret.floor();
}
- // Compute the tile offset, and if we might the output for a neighbor top tile
+ // Compute the tile offset, and if we might the output for a neighbor top tile.
Vector2 in_tile_pos = raw_pos - ret;
bool in_top_left_triangle = (in_tile_pos - Vector2(0.0, 0.5)).cross(Vector2(1.0 / overlapping_ratio - 1, -0.5)) > 0;
bool in_bottom_left_triangle = (in_tile_pos - Vector2(0.0, 0.5)).cross(Vector2(1.0 / overlapping_ratio - 1, 0.5)) <= 0;
@@ -3546,7 +3773,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh
default:
ERR_FAIL_V(p_coords);
}
- } else { // Half-offset shapes (square and hexagon)
+ } else { // Half-offset shapes (square and hexagon).
switch (tile_set->get_tile_layout()) {
case TileSet::TILE_LAYOUT_STACKED: {
if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
@@ -3856,63 +4083,23 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh
}
TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TypedArray<Vector2i>());
-
- // Returns the cells used in the tilemap.
- TypedArray<Vector2i> a;
- a.resize(layers[p_layer].tile_map.size());
- int i = 0;
- for (const KeyValue<Vector2i, TileMapCell> &E : layers[p_layer].tile_map) {
- Vector2i p(E.key.x, E.key.y);
- a[i++] = p;
- }
-
- return a;
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, TypedArray<Vector2i>(), get_used_cells);
}
TypedArray<Vector2i> TileMap::get_used_cells_by_id(int p_layer, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) const {
- ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TypedArray<Vector2i>());
-
- // Returns the cells used in the tilemap.
- TypedArray<Vector2i> a;
- for (const KeyValue<Vector2i, TileMapCell> &E : layers[p_layer].tile_map) {
- if ((p_source_id == TileSet::INVALID_SOURCE || p_source_id == E.value.source_id) &&
- (p_atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || p_atlas_coords == E.value.get_atlas_coords()) &&
- (p_alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE || p_alternative_tile == E.value.alternative_tile)) {
- a.push_back(E.key);
- }
- }
-
- return a;
+ TILEMAP_CALL_FOR_LAYER_V(p_layer, TypedArray<Vector2i>(), get_used_cells_by_id);
}
-Rect2i TileMap::get_used_rect() { // Not const because of cache
- // Return the rect of the currently used area
- if (used_rect_cache_dirty) {
- bool first = true;
- used_rect_cache = Rect2i();
-
- for (unsigned int i = 0; i < layers.size(); i++) {
- const HashMap<Vector2i, TileMapCell> &tile_map = layers[i].tile_map;
- if (tile_map.size() > 0) {
- if (first) {
- used_rect_cache = Rect2i(tile_map.begin()->key.x, tile_map.begin()->key.y, 0, 0);
- first = false;
- }
-
- for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
- used_rect_cache.expand_to(Vector2i(E.key.x, E.key.y));
- }
- }
- }
-
- if (!first) { // first is true if every layer is empty.
- used_rect_cache.size += Vector2i(1, 1); // The cache expands to top-left coordinate, so we add one full tile.
- }
- used_rect_cache_dirty = false;
+Rect2i TileMap::get_used_rect() const {
+ // Return the visible rect of the tilemap.
+ if (layers.is_empty()) {
+ return Rect2i();
}
-
- return used_rect_cache;
+ Rect2 rect = layers[0]->get_used_rect();
+ for (unsigned int i = 1; i < layers.size(); i++) {
+ rect = rect.merge(layers[i]->get_used_rect());
+ }
+ return rect;
}
// --- Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems ---
@@ -3920,13 +4107,8 @@ Rect2i TileMap::get_used_rect() { // Not const because of cache
void TileMap::set_light_mask(int p_light_mask) {
// Occlusion: set light mask.
CanvasItem::set_light_mask(p_light_mask);
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
- for (const RID &ci : E.value.canvas_items) {
- RenderingServer::get_singleton()->canvas_item_set_light_mask(ci, get_light_mask());
- }
- }
- _rendering_update_layer(layer);
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_light_mask_changed();
}
}
@@ -3935,14 +4117,8 @@ void TileMap::set_material(const Ref<Material> &p_material) {
CanvasItem::set_material(p_material);
// Update material for the whole tilemap.
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
- TileMapQuadrant &q = E.value;
- for (const RID &ci : q.canvas_items) {
- RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid());
- }
- }
- _rendering_update_layer(layer);
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_material_changed();
}
}
@@ -3951,46 +4127,24 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) {
CanvasItem::set_use_parent_material(p_use_parent_material);
// Update use_parent_material for the whole tilemap.
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
- TileMapQuadrant &q = E.value;
- for (const RID &ci : q.canvas_items) {
- RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid());
- }
- }
- _rendering_update_layer(layer);
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_use_parent_material_changed();
}
}
void TileMap::set_texture_filter(TextureFilter p_texture_filter) {
// Set a default texture filter for the whole tilemap.
CanvasItem::set_texture_filter(p_texture_filter);
- TextureFilter target_filter = get_texture_filter_in_tree();
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = layers[layer].quadrant_map.begin(); F; ++F) {
- TileMapQuadrant &q = F->value;
- for (const RID &ci : q.canvas_items) {
- RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(target_filter));
- _make_quadrant_dirty(F);
- }
- }
- _rendering_update_layer(layer);
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_texture_filter_changed();
}
}
void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) {
// Set a default texture repeat for the whole tilemap.
CanvasItem::set_texture_repeat(p_texture_repeat);
- TextureRepeat target_repeat = get_texture_repeat_in_tree();
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = layers[layer].quadrant_map.begin(); F; ++F) {
- TileMapQuadrant &q = F->value;
- for (const RID &ci : q.canvas_items) {
- RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(target_repeat));
- _make_quadrant_dirty(F);
- }
- }
- _rendering_update_layer(layer);
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->notify_texture_repeat_changed();
}
}
@@ -4088,15 +4242,15 @@ PackedStringArray TileMap::get_configuration_warnings() const {
// Retrieve the set of Z index values with a Y-sorted layer.
RBSet<int> y_sorted_z_index;
- for (const TileMapLayer &layer : layers) {
- if (layer.y_sort_enabled) {
- y_sorted_z_index.insert(layer.z_index);
+ for (const Ref<TileMapLayer> &layer : layers) {
+ if (layer->is_y_sort_enabled()) {
+ y_sorted_z_index.insert(layer->get_z_index());
}
}
// Check if we have a non-sorted layer in a Z-index with a Y-sorted layer.
- for (const TileMapLayer &layer : layers) {
- if (!layer.y_sort_enabled && y_sorted_z_index.has(layer.z_index)) {
+ for (const Ref<TileMapLayer> &layer : layers) {
+ if (!layer->is_y_sort_enabled() && y_sorted_z_index.has(layer->get_z_index())) {
warnings.push_back(RTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers."));
break;
}
@@ -4104,8 +4258,8 @@ PackedStringArray TileMap::get_configuration_warnings() const {
// Check if Y-sort is enabled on a layer but not on the node.
if (!is_y_sort_enabled()) {
- for (const TileMapLayer &layer : layers) {
- if (layer.y_sort_enabled) {
+ for (const Ref<TileMapLayer> &layer : layers) {
+ if (layer->is_y_sort_enabled()) {
warnings.push_back(RTR("A TileMap layer is set as Y-sorted, but Y-sort is not enabled on the TileMap node itself."));
break;
}
@@ -4116,8 +4270,8 @@ PackedStringArray TileMap::get_configuration_warnings() const {
if (tile_set.is_valid() && tile_set->get_tile_shape() == TileSet::TILE_SHAPE_ISOMETRIC) {
bool warn = !is_y_sort_enabled();
if (!warn) {
- for (const TileMapLayer &layer : layers) {
- if (!layer.y_sort_enabled) {
+ for (const Ref<TileMapLayer> &layer : layers) {
+ if (!layer->is_y_sort_enabled()) {
warn = true;
break;
}
@@ -4155,6 +4309,13 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_layer_y_sort_origin", "layer"), &TileMap::get_layer_y_sort_origin);
ClassDB::bind_method(D_METHOD("set_layer_z_index", "layer", "z_index"), &TileMap::set_layer_z_index);
ClassDB::bind_method(D_METHOD("get_layer_z_index", "layer"), &TileMap::get_layer_z_index);
+ ClassDB::bind_method(D_METHOD("set_layer_navigation_map", "layer", "map"), &TileMap::set_layer_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_layer_navigation_map", "layer"), &TileMap::get_layer_navigation_map);
+
+#ifndef DISABLE_DEPRECATED
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "layer", "map"), &TileMap::set_layer_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map", "layer"), &TileMap::get_layer_navigation_map);
+#endif // DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_collision_animatable", "enabled"), &TileMap::set_collision_animatable);
ClassDB::bind_method(D_METHOD("is_collision_animatable"), &TileMap::is_collision_animatable);
@@ -4164,9 +4325,6 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "navigation_visibility_mode"), &TileMap::set_navigation_visibility_mode);
ClassDB::bind_method(D_METHOD("get_navigation_visibility_mode"), &TileMap::get_navigation_visibility_mode);
- ClassDB::bind_method(D_METHOD("set_navigation_map", "layer", "map"), &TileMap::set_navigation_map);
- ClassDB::bind_method(D_METHOD("get_navigation_map", "layer"), &TileMap::get_navigation_map);
-
ClassDB::bind_method(D_METHOD("set_cell", "layer", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(0));
ClassDB::bind_method(D_METHOD("erase_cell", "layer", "coords"), &TileMap::erase_cell);
ClassDB::bind_method(D_METHOD("get_cell_source_id", "layer", "coords", "use_proxies"), &TileMap::get_cell_source_id, DEFVAL(false));
@@ -4216,9 +4374,9 @@ void TileMap::_bind_methods() {
ADD_ARRAY("layers", "layer_");
- ADD_PROPERTY_DEFAULT("format", FORMAT_1);
+ ADD_PROPERTY_DEFAULT("format", TileMapLayer::FORMAT_1);
- ADD_SIGNAL(MethodInfo("changed"));
+ ADD_SIGNAL(MethodInfo(CoreStringNames::get_singleton()->changed));
BIND_ENUM_CONSTANT(VISIBILITY_MODE_DEFAULT);
BIND_ENUM_CONSTANT(VISIBILITY_MODE_FORCE_HIDE);
@@ -4226,9 +4384,11 @@ void TileMap::_bind_methods() {
}
void TileMap::_tile_set_changed() {
- emit_signal(SNAME("changed"));
+ emit_signal(CoreStringNames::get_singleton()->changed);
_tile_set_changed_deferred_update_needed = true;
- instantiated_scenes.clear();
+ for (Ref<TileMapLayer> &layer : layers) {
+ layer->clear_instantiated_scenes();
+ }
call_deferred(SNAME("_tile_set_changed_deferred_update"));
update_configuration_warnings();
}
@@ -4245,13 +4405,20 @@ TileMap::TileMap() {
set_notify_transform(true);
set_notify_local_transform(false);
- layers.resize(1);
+ Ref<TileMapLayer> new_layer;
+ new_layer.instantiate();
+ new_layer->set_tile_map(this);
+ new_layer->set_layer_index_in_tile_map_node(0);
+ layers.push_back(new_layer);
}
TileMap::~TileMap() {
if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed));
+ tile_set->disconnect_changed(callable_mp(this, &TileMap::_tile_set_changed));
}
_clear_internals();
}
+
+#undef TILEMAP_CALL_FOR_LAYER
+#undef TILEMAP_CALL_FOR_LAYER_V
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 13c0eb4a95..0ad47c51da 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -37,6 +37,58 @@
class TileSetAtlasSource;
+class TerrainConstraint {
+private:
+ const TileMap *tile_map = nullptr;
+ Vector2i base_cell_coords;
+ int bit = -1;
+ int terrain = -1;
+
+ int priority = 1;
+
+public:
+ bool operator<(const TerrainConstraint &p_other) const {
+ if (base_cell_coords == p_other.base_cell_coords) {
+ return bit < p_other.bit;
+ }
+ return base_cell_coords < p_other.base_cell_coords;
+ }
+
+ String to_string() const {
+ return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priority:%d}", base_cell_coords, bit, terrain, priority);
+ }
+
+ Vector2i get_base_cell_coords() const {
+ return base_cell_coords;
+ }
+
+ bool is_center_bit() const {
+ return bit == 0;
+ }
+
+ HashMap<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
+
+ void set_terrain(int p_terrain) {
+ terrain = p_terrain;
+ }
+
+ int get_terrain() const {
+ return terrain;
+ }
+
+ void set_priority(int p_priority) {
+ priority = p_priority;
+ }
+
+ int get_priority() const {
+ return priority;
+ }
+
+ TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain); // For the center terrain bit
+ TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits
+ TerrainConstraint(){};
+};
+
struct TileMapQuadrant {
struct CoordsWorldComparator {
_ALWAYS_INLINE_ bool operator()(const Vector2 &p_a, const Vector2 &p_b) const {
@@ -52,13 +104,12 @@ struct TileMapQuadrant {
// Dirty list element.
SelfList<TileMapQuadrant> dirty_list_element;
- // Quadrant layer and coords.
- int layer = -1;
+ // Quadrant coords.
Vector2i coords;
- // TileMapCells
+ // TileMapCells.
RBSet<Vector2i> cells;
- // We need those two maps to sort by local position for rendering
+ // We need those two maps to sort by local position for rendering.
// This is kind of workaround, it would be better to sort the cells directly in the "cells" set instead.
RBMap<Vector2i, Vector2> map_to_local;
RBMap<Vector2, Vector2i, CoordsWorldComparator> local_to_map;
@@ -83,7 +134,6 @@ struct TileMapQuadrant {
HashMap<Vector2i, TileData *> runtime_tile_data_cache;
void operator=(const TileMapQuadrant &q) {
- layer = q.layer;
coords = q.coords;
debug_canvas_item = q.debug_canvas_item;
canvas_items = q.canvas_items;
@@ -94,7 +144,6 @@ struct TileMapQuadrant {
TileMapQuadrant(const TileMapQuadrant &q) :
dirty_list_element(this) {
- layer = q.layer;
coords = q.coords;
debug_canvas_item = q.debug_canvas_item;
canvas_items = q.canvas_items;
@@ -108,62 +157,174 @@ struct TileMapQuadrant {
}
};
-class TileMap : public Node2D {
- GDCLASS(TileMap, Node2D);
-
+class TileMapLayer : public RefCounted {
public:
- class TerrainConstraint {
- private:
- const TileMap *tile_map;
- Vector2i base_cell_coords;
- int bit = -1;
- int terrain = -1;
-
- int priority = 1;
-
- public:
- bool operator<(const TerrainConstraint &p_other) const {
- if (base_cell_coords == p_other.base_cell_coords) {
- return bit < p_other.bit;
- }
- return base_cell_coords < p_other.base_cell_coords;
- }
+ enum DataFormat {
+ FORMAT_1 = 0,
+ FORMAT_2,
+ FORMAT_3,
+ FORMAT_MAX,
+ };
- String to_string() const {
- return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priority:%d}", base_cell_coords, bit, terrain, priority);
- }
+private:
+ // Exposed properties.
+ String name;
+ bool enabled = true;
+ Color modulate = Color(1, 1, 1, 1);
+ bool y_sort_enabled = false;
+ int y_sort_origin = 0;
+ int z_index = 0;
+ RID navigation_map;
+ bool uses_world_navigation_map = false;
+
+ // Internal.
+ TileMap *tile_map_node = nullptr;
+ int layer_index_in_tile_map_node = -1;
+ RID canvas_item;
+ bool _rendering_quadrant_order_dirty = false;
+ HashMap<Vector2i, TileMapCell> tile_map;
+ HashMap<Vector2i, TileMapQuadrant> quadrant_map;
+ SelfList<TileMapQuadrant>::List dirty_quadrant_list;
+
+ // Rect cache.
+ mutable Rect2 rect_cache;
+ mutable bool rect_cache_dirty = true;
+ mutable Rect2i used_rect_cache;
+ mutable bool used_rect_cache_dirty = true;
+
+ // Quadrants management.
+ Vector2i _coords_to_quadrant_coords(const Vector2i &p_coords) const;
+ HashMap<Vector2i, TileMapQuadrant>::Iterator _create_quadrant(const Vector2i &p_qk);
+ void _make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator Q);
+ void _erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q);
- Vector2i get_base_cell_coords() const {
- return base_cell_coords;
- }
+ // Per-system methods.
+ void _rendering_notification(int p_what);
+ void _rendering_update();
+ void _rendering_cleanup();
+ void _rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
+ void _rendering_reorder_quadrants(int &r_index);
+ void _rendering_create_quadrant(TileMapQuadrant *p_quadrant);
+ void _rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant);
+ void _rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
- bool is_center_bit() const {
- return bit == 0;
- }
+ HashMap<RID, Vector2i> bodies_coords; // Mapping for RID to coords.
+ void _physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
+ void _physics_cleanup_quadrant(TileMapQuadrant *p_quadrant);
+ void _physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
- HashMap<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
+ void _navigation_update();
+ void _navigation_cleanup();
+ void _navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
+ void _navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant);
+ void _navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
- void set_terrain(int p_terrain) {
- terrain = p_terrain;
- }
+ HashSet<Vector2i> instantiated_scenes;
+ void _scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
+ void _scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant);
+ void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
- int get_terrain() const {
- return terrain;
- }
+ // Runtime tile data.
+ void _build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
- void set_priority(int p_priority) {
- priority = p_priority;
- }
+ // Terrains.
+ TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern);
+ RBSet<TerrainConstraint> _get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
+ RBSet<TerrainConstraint> _get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const;
- int get_priority() const {
- return priority;
- }
+public:
+ // TileMap node.
+ void set_tile_map(TileMap *p_tile_map);
+ void set_layer_index_in_tile_map_node(int p_index);
- TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain); // For the center terrain bit
- TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits
- TerrainConstraint(){};
- };
+ // Rect caching.
+ Rect2 get_rect(bool &r_changed) const;
+ // Terrains.
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints); // Not exposed.
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed.
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed.
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true); // Not exposed.
+
+ // Not exposed to users.
+ TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const;
+ int get_effective_quadrant_size() const;
+
+ // For TileMap node's use.
+ void notify_canvas_entered();
+ void notify_visibility_changed();
+ void notify_xform_changed();
+ void notify_local_xform_changed();
+ void notify_canvas_exited();
+ void notify_selected_layer_changed();
+ void notify_light_mask_changed();
+ void notify_material_changed();
+ void notify_use_parent_material_changed();
+ void notify_texture_filter_changed();
+ void notify_texture_repeat_changed();
+ void update_dirty_quadrants();
+ void set_tile_data(DataFormat p_format, const Vector<int> &p_data);
+ Vector<int> get_tile_data() const;
+ void clear_instantiated_scenes();
+ void clear_internals(); // Exposed for now to tilemap, but ideally, we should avoid it.
+ void recreate_internals(); // Exposed for now to tilemap, but ideally, we should avoid it.
+
+ // --- Exposed in TileMap ---
+
+ // Cells manipulation.
+ void set_cell(const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0);
+ void erase_cell(const Vector2i &p_coords);
+
+ int get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies = false) const;
+ Vector2i get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies = false) const;
+ int get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies = false) const;
+ TileData *get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies = false) const; // Helper method to make accessing the data easier.
+ void clear();
+
+ // Patterns.
+ Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array);
+ void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern);
+
+ // Terrains.
+ void set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
+ void set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
+
+ // Cells usage.
+ TypedArray<Vector2i> get_used_cells() const;
+ 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;
+
+ // Layer properties.
+ void set_name(String p_name);
+ String get_name() const;
+ void set_enabled(bool p_enabled);
+ bool is_enabled() const;
+ void set_modulate(Color p_modulate);
+ Color get_modulate() const;
+ void set_y_sort_enabled(bool p_y_sort_enabled);
+ bool is_y_sort_enabled() const;
+ void set_y_sort_origin(int p_y_sort_origin);
+ int get_y_sort_origin() const;
+ void set_z_index(int p_z_index);
+ int get_z_index() const;
+ void set_navigation_map(RID p_map);
+ RID get_navigation_map() const;
+
+ // In case something goes wrong.
+ void force_update();
+
+ // Fixing and clearing methods.
+ void fix_invalid_tiles();
+
+ // Find coords for body.
+ bool has_body_rid(RID p_physics_body) const;
+ Vector2i get_coords_for_body_rid(RID p_physics_body) const; // For finding tiles from collision.
+};
+
+class TileMap : public Node2D {
+ GDCLASS(TileMap, Node2D);
+
+public:
enum VisibilityMode {
VISIBILITY_MODE_DEFAULT,
VISIBILITY_MODE_FORCE_SHOW,
@@ -174,12 +335,7 @@ private:
friend class TileSetPlugin;
// A compatibility enum to specify how is the data if formatted.
- enum DataFormat {
- FORMAT_1 = 0,
- FORMAT_2,
- FORMAT_3
- };
- mutable DataFormat format = FORMAT_3;
+ mutable TileMapLayer::DataFormat format = TileMapLayer::FORMAT_3;
static constexpr float FP_ADJUST = 0.00001;
@@ -190,99 +346,17 @@ private:
VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT;
VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT;
- // Updates.
- bool pending_update = false;
-
- // Rect.
- Rect2 rect_cache;
- bool rect_cache_dirty = true;
- Rect2i used_rect_cache;
- bool used_rect_cache_dirty = true;
-
- // TileMap layers.
- struct TileMapLayer {
- String name;
- bool enabled = true;
- Color modulate = Color(1, 1, 1, 1);
- bool y_sort_enabled = false;
- int y_sort_origin = 0;
- int z_index = 0;
- RID canvas_item;
- HashMap<Vector2i, TileMapCell> tile_map;
- HashMap<Vector2i, TileMapQuadrant> quadrant_map;
- SelfList<TileMapQuadrant>::List dirty_quadrant_list;
- RID navigation_map;
- bool uses_world_navigation_map = false;
- };
- LocalVector<TileMapLayer> layers;
+ // Layers.
+ LocalVector<Ref<TileMapLayer>> layers;
int selected_layer = -1;
- // Mapping for RID to coords.
- HashMap<RID, Vector2i> bodies_coords;
- // Mapping for RID to tile layer.
- HashMap<RID, int> bodies_layers;
-
- // Quadrants and internals management.
- Vector2i _coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const;
-
- HashMap<Vector2i, TileMapQuadrant>::Iterator _create_quadrant(int p_layer, const Vector2i &p_qk);
-
- void _make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator Q);
- void _make_all_quadrants_dirty();
- void _queue_update_dirty_quadrants();
-
- void _update_dirty_quadrants();
-
- void _recreate_layer_internals(int p_layer);
- void _recreate_internals();
-
- void _erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q);
- void _clear_layer_internals(int p_layer);
void _clear_internals();
+ void _recreate_internals();
- HashSet<Vector3i> instantiated_scenes;
-
- // Rect caching.
- void _recompute_rect_cache();
-
- // Per-system methods.
- bool _rendering_quadrant_order_dirty = false;
- void _rendering_notification(int p_what);
- void _rendering_update_layer(int p_layer);
- void _rendering_cleanup_layer(int p_layer);
- void _rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
- void _rendering_create_quadrant(TileMapQuadrant *p_quadrant);
- void _rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant);
- void _rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
+ bool pending_update = false;
Transform2D last_valid_transform;
Transform2D new_transform;
- void _physics_notification(int p_what);
- void _physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
- void _physics_cleanup_quadrant(TileMapQuadrant *p_quadrant);
- void _physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
-
- void _navigation_notification(int p_what);
- void _navigation_update_layer(int p_layer);
- void _navigation_cleanup_layer(int p_layer);
- void _navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
- void _navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant);
- void _navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
-
- void _scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
- void _scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant);
- void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
-
- // Terrains.
- TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern);
- RBSet<TerrainConstraint> _get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
- RBSet<TerrainConstraint> _get_terrain_constraints_from_painted_cells_list(int p_layer, const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const;
-
- // Set and get tiles from data arrays.
- void _set_tile_data(int p_layer, const Vector<int> &p_data);
- Vector<int> _get_tile_data(int p_layer) const;
-
- void _build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
void _tile_set_changed();
bool _tile_set_changed_deferred_update_needed = false;
@@ -296,17 +370,22 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ Rect2i _get_used_rect_bind_compat_78328();
+ static void _bind_compatibility_methods();
+#endif
+
public:
static Vector2i transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout);
- enum {
- INVALID_CELL = -1
- };
-
#ifdef TOOLS_ENABLED
virtual Rect2 _edit_get_rect() const override;
#endif
+ // Called by TileMapLayers.
+ void queue_update_dirty_quadrants();
+ void _update_dirty_quadrants();
+
void set_tileset(const Ref<TileSet> &p_tileset);
Ref<TileSet> get_tileset() const;
@@ -320,6 +399,7 @@ public:
void add_layer(int p_to_pos);
void move_layer(int p_layer, int p_to_pos);
void remove_layer(int p_layer);
+
void set_layer_name(int p_layer, String p_name);
String get_layer_name(int p_layer) const;
void set_layer_enabled(int p_layer, bool p_visible);
@@ -332,6 +412,9 @@ public:
int get_layer_y_sort_origin(int p_layer) const;
void set_layer_z_index(int p_layer, int p_z_index);
int get_layer_z_index(int p_layer) const;
+ void set_layer_navigation_map(int p_layer, RID p_map);
+ RID get_layer_navigation_map(int p_layer) const;
+
void set_selected_layer(int p_layer_id); // For editor use.
int get_selected_layer() const;
@@ -345,9 +428,6 @@ public:
void set_navigation_visibility_mode(VisibilityMode p_show_navigation);
VisibilityMode get_navigation_visibility_mode();
- void set_navigation_map(int p_layer, RID p_map);
- RID get_navigation_map(int p_layer) const;
-
// Cells accessors.
void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0);
void erase_cell(int p_layer, const Vector2i &p_coords);
@@ -362,20 +442,19 @@ public:
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
void set_pattern(int p_layer, const Vector2i &p_position, const Ref<TileMapPattern> p_pattern);
- // Terrains.
- HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(int p_layer, const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints); // Not exposed.
- HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed.
- HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed.
- HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true); // Not exposed.
+ // Terrains (Not exposed).
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(int p_layer, const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints);
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true);
+ // Terrains (exposed).
void set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
void set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true);
- // Not exposed to users
+ // Not exposed to users.
TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
- HashMap<Vector2i, TileMapQuadrant> *get_quadrant_map(int p_layer);
int get_effective_quadrant_size(int p_layer) const;
- //---
virtual void set_y_sort_enabled(bool p_enable) override;
@@ -387,9 +466,9 @@ public:
TypedArray<Vector2i> get_used_cells(int p_layer) const;
TypedArray<Vector2i> get_used_cells_by_id(int p_layer, 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(); // Not const because of cache
+ Rect2i get_used_rect() const;
- // Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems
+ // Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems.
virtual void set_light_mask(int p_light_mask) override;
virtual void set_material(const Ref<Material> &p_material) override;
virtual void set_use_parent_material(bool p_use_parent_material) override;
@@ -404,18 +483,18 @@ public:
// Fixing and clearing methods.
void fix_invalid_tiles();
- // Clears tiles from a given layer
+ // Clears tiles from a given layer.
void clear_layer(int p_layer);
void clear();
- // Force a TileMap update
+ // Force a TileMap update.
void force_update(int p_layer = -1);
// Helpers?
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &coords);
void draw_cells_outline(Control *p_control, const RBSet<Vector2i> &p_cells, Color p_color, Transform2D p_transform = Transform2D());
- // Virtual function to modify the TileData at runtime
+ // Virtual function to modify the TileData at runtime.
GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i);
GDVIRTUAL3(_tile_data_runtime_update, int, Vector2i, TileData *);
diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp
index 10a9e7edec..5ed7fadb2a 100644
--- a/scene/2d/touch_screen_button.cpp
+++ b/scene/2d/touch_screen_button.cpp
@@ -82,11 +82,11 @@ void TouchScreenButton::set_shape(const Ref<Shape2D> &p_shape) {
return;
}
if (shape.is_valid()) {
- shape->disconnect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
+ shape->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
}
shape = p_shape;
if (shape.is_valid()) {
- shape->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
+ shape->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
}
queue_redraw();
}
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 225b9b35b3..37ceb9d1a1 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -31,7 +31,6 @@
#include "camera_3d.h"
#include "collision_object_3d.h"
-#include "core/core_string_names.h"
#include "core/math/projection.h"
#include "scene/main/viewport.h"
@@ -430,7 +429,7 @@ void Camera3D::set_attributes(const Ref<CameraAttributes> &p_attributes) {
if (attributes.is_valid()) {
CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr());
if (physical_attributes) {
- attributes->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Camera3D::_attributes_changed));
+ attributes->disconnect_changed(callable_mp(this, &Camera3D::_attributes_changed));
}
}
@@ -439,7 +438,7 @@ void Camera3D::set_attributes(const Ref<CameraAttributes> &p_attributes) {
if (attributes.is_valid()) {
CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr());
if (physical_attributes) {
- attributes->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Camera3D::_attributes_changed));
+ attributes->connect_changed(callable_mp(this, &Camera3D::_attributes_changed));
_attributes_changed();
}
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index 6d8d60dcaa..bfe594adc2 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -394,11 +394,7 @@ void CollisionObject3D::_update_debug_shapes() {
if (s.debug_shape.is_null()) {
s.debug_shape = RS::get_singleton()->instance_create();
RS::get_singleton()->instance_set_scenario(s.debug_shape, get_world_3d()->get_scenario());
-
- if (!s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_shape_changed))) {
- s.shape->connect("changed", callable_mp(this, &CollisionObject3D::_shape_changed).bind(s.shape), CONNECT_DEFERRED);
- }
-
+ s.shape->connect_changed(callable_mp(this, &CollisionObject3D::_shape_changed).bind(s.shape), CONNECT_DEFERRED);
++debug_shapes_count;
}
@@ -422,8 +418,8 @@ void CollisionObject3D::_clear_debug_shapes() {
if (s.debug_shape.is_valid()) {
RS::get_singleton()->free(s.debug_shape);
s.debug_shape = RID();
- if (s.shape.is_valid() && s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_update_shape_data))) {
- s.shape->disconnect("changed", callable_mp(this, &CollisionObject3D::_update_shape_data));
+ if (s.shape.is_valid()) {
+ s.shape->disconnect_changed(callable_mp(this, &CollisionObject3D::_update_shape_data));
}
}
}
@@ -663,8 +659,8 @@ void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape)
if (s.debug_shape.is_valid()) {
RS::get_singleton()->free(s.debug_shape);
- if (s.shape.is_valid() && s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_shape_changed))) {
- s.shape->disconnect("changed", callable_mp(this, &CollisionObject3D::_shape_changed));
+ if (s.shape.is_valid()) {
+ s.shape->disconnect_changed(callable_mp(this, &CollisionObject3D::_shape_changed));
}
--debug_shapes_count;
}
diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp
index 10eefc784d..0bb0382301 100644
--- a/scene/3d/collision_shape_3d.cpp
+++ b/scene/3d/collision_shape_3d.cpp
@@ -112,9 +112,10 @@ void CollisionShape3D::_notification(int p_what) {
}
}
+#ifndef DISABLE_DEPRECATED
void CollisionShape3D::resource_changed(Ref<Resource> res) {
- update_gizmos();
}
+#endif
PackedStringArray CollisionShape3D::get_configuration_warnings() const {
PackedStringArray warnings = Node::get_configuration_warnings();
@@ -145,8 +146,9 @@ PackedStringArray CollisionShape3D::get_configuration_warnings() const {
}
void CollisionShape3D::_bind_methods() {
- //not sure if this should do anything
+#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &CollisionShape3D::resource_changed);
+#endif
ClassDB::bind_method(D_METHOD("set_shape", "shape"), &CollisionShape3D::set_shape);
ClassDB::bind_method(D_METHOD("get_shape"), &CollisionShape3D::get_shape);
ClassDB::bind_method(D_METHOD("set_disabled", "enable"), &CollisionShape3D::set_disabled);
@@ -162,12 +164,12 @@ void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) {
if (p_shape == shape) {
return;
}
- if (!shape.is_null()) {
- shape->unregister_owner(this);
+ if (shape.is_valid()) {
+ shape->disconnect_changed(callable_mp((Node3D *)this, &Node3D::update_gizmos));
}
shape = p_shape;
- if (!shape.is_null()) {
- shape->register_owner(this);
+ if (shape.is_valid()) {
+ shape->connect_changed(callable_mp((Node3D *)this, &Node3D::update_gizmos));
}
update_gizmos();
if (collision_object) {
@@ -206,8 +208,5 @@ CollisionShape3D::CollisionShape3D() {
}
CollisionShape3D::~CollisionShape3D() {
- if (!shape.is_null()) {
- shape->unregister_owner(this);
- }
//RenderingServer::get_singleton()->free(indicator);
}
diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h
index 74928bad6d..bc0e70f8ac 100644
--- a/scene/3d/collision_shape_3d.h
+++ b/scene/3d/collision_shape_3d.h
@@ -43,7 +43,9 @@ class CollisionShape3D : public Node3D {
uint32_t owner_id = 0;
CollisionObject3D *collision_object = nullptr;
+#ifndef DISABLE_DEPRECATED
void resource_changed(Ref<Resource> res);
+#endif
bool disabled = false;
protected:
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index 405d478a47..3dc82cfb97 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -33,7 +33,11 @@
#include "scene/3d/camera_3d.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/main/viewport.h"
+#include "scene/resources/curve_texture.h"
+#include "scene/resources/gradient_texture.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/particle_process_material.h"
+#include "scene/scene_string_names.h"
AABB CPUParticles3D::get_aabb() const {
return AABB();
@@ -46,6 +50,7 @@ void CPUParticles3D::set_emitting(bool p_emitting) {
emitting = p_emitting;
if (emitting) {
+ active = true;
set_process_internal(true);
// first update before rendering to avoid one frame delay after emitting starts
@@ -220,7 +225,6 @@ PackedStringArray CPUParticles3D::get_configuration_warnings() const {
void CPUParticles3D::restart() {
time = 0;
- inactive_time = 0;
frame_remainder = 0;
cycle = 0;
emitting = false;
@@ -575,21 +579,15 @@ void CPUParticles3D::_update_internal() {
}
double delta = get_process_delta_time();
- if (emitting) {
- inactive_time = 0;
- } else {
- inactive_time += delta;
- if (inactive_time > lifetime * 1.2) {
- set_process_internal(false);
- _set_redraw(false);
+ if (!active && !emitting) {
+ set_process_internal(false);
+ _set_redraw(false);
- //reset variables
- time = 0;
- inactive_time = 0;
- frame_remainder = 0;
- cycle = 0;
- return;
- }
+ //reset variables
+ time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+ return;
}
_set_redraw(true);
@@ -670,6 +668,7 @@ void CPUParticles3D::_particles_process(double p_delta) {
double system_phase = time / lifetime;
+ bool should_be_active = false;
for (int i = 0; i < pcount; i++) {
Particle &p = parray[i];
@@ -1136,6 +1135,12 @@ void CPUParticles3D::_particles_process(double p_delta) {
}
p.transform.origin += p.velocity * local_delta;
+
+ should_be_active = true;
+ }
+ if (!Math::is_equal_approx(time, 0.0) && active && !should_be_active) {
+ active = false;
+ emit_signal(SceneStringNames::get_singleton()->finished);
}
}
@@ -1543,6 +1548,8 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles3D::convert_from_particles);
+ ADD_SIGNAL(MethodInfo("finished"));
+
ADD_GROUP("Emission Shape", "emission_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Sphere Surface,Box,Points,Directed Points,Ring", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h
index 40ea4e8cdf..a5bc7dddb9 100644
--- a/scene/3d/cpu_particles_3d.h
+++ b/scene/3d/cpu_particles_3d.h
@@ -81,6 +81,7 @@ public:
private:
bool emitting = false;
+ bool active = false;
struct Particle {
Transform3D transform;
@@ -101,7 +102,6 @@ private:
};
double time = 0.0;
- double inactive_time = 0.0;
double frame_remainder = 0.0;
int cycle = 0;
bool redraw = false;
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index 4d0bc8b02f..3a23cbcff1 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -31,19 +31,37 @@
#include "gpu_particles_3d.h"
#include "scene/resources/particle_process_material.h"
+#include "scene/scene_string_names.h"
AABB GPUParticles3D::get_aabb() const {
return AABB();
}
void GPUParticles3D::set_emitting(bool p_emitting) {
- RS::get_singleton()->particles_set_emitting(particles, p_emitting);
+ // Do not return even if `p_emitting == emitting` because `emitting` is just an approximation.
if (p_emitting && one_shot) {
+ if (!active && !emitting) {
+ // Last cycle ended.
+ active = true;
+ time = 0;
+ signal_cancled = false;
+ emission_time = lifetime;
+ active_time = lifetime * (2 - explosiveness_ratio);
+ } else {
+ signal_cancled = true;
+ }
set_process_internal(true);
} else if (!p_emitting) {
- set_process_internal(false);
+ if (one_shot) {
+ set_process_internal(true);
+ } else {
+ set_process_internal(false);
+ }
}
+
+ emitting = p_emitting;
+ RS::get_singleton()->particles_set_emitting(particles, p_emitting);
}
void GPUParticles3D::set_amount(int p_amount) {
@@ -122,7 +140,7 @@ void GPUParticles3D::set_collision_base_size(real_t p_size) {
}
bool GPUParticles3D::is_emitting() const {
- return RS::get_singleton()->particles_get_emitting(particles);
+ return emitting;
}
int GPUParticles3D::get_amount() const {
@@ -216,13 +234,13 @@ void GPUParticles3D::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) {
ERR_FAIL_INDEX(p_pass, draw_passes.size());
if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) {
- draw_passes.write[p_pass]->disconnect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings));
+ draw_passes.write[p_pass]->disconnect_changed(callable_mp((Node *)this, &Node::update_configuration_warnings));
}
draw_passes.write[p_pass] = p_mesh;
if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) {
- draw_passes.write[p_pass]->connect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings), CONNECT_DEFERRED);
+ draw_passes.write[p_pass]->connect_changed(callable_mp((Node *)this, &Node::update_configuration_warnings), CONNECT_DEFERRED);
}
RID mesh_rid;
@@ -373,6 +391,16 @@ PackedStringArray GPUParticles3D::get_configuration_warnings() const {
void GPUParticles3D::restart() {
RenderingServer::get_singleton()->particles_restart(particles);
RenderingServer::get_singleton()->particles_set_emitting(particles, true);
+
+ emitting = true;
+ active = true;
+ signal_cancled = false;
+ time = 0;
+ emission_time = lifetime * (1 - explosiveness_ratio);
+ active_time = lifetime * (2 - explosiveness_ratio);
+ if (one_shot) {
+ set_process_internal(true);
+ }
}
AABB GPUParticles3D::capture_aabb() const {
@@ -425,9 +453,23 @@ void GPUParticles3D::_notification(int p_what) {
// Use internal process when emitting and one_shot is on so that when
// the shot ends the editor can properly update.
case NOTIFICATION_INTERNAL_PROCESS: {
- if (one_shot && !is_emitting()) {
- notify_property_list_changed();
- set_process_internal(false);
+ if (one_shot) {
+ time += get_process_delta_time();
+ if (time > emission_time) {
+ emitting = false;
+ if (!active) {
+ set_process_internal(false);
+ }
+ }
+ if (time > active_time) {
+ if (active && !signal_cancled) {
+ emit_signal(SceneStringNames::get_singleton()->finished);
+ }
+ active = false;
+ if (!emitting) {
+ set_process_internal(false);
+ }
+ }
}
} break;
@@ -571,6 +613,8 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align);
ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align);
+ ADD_SIGNAL(MethodInfo("finished"));
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false.
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h
index 474f5500f8..dba6a8f2ab 100644
--- a/scene/3d/gpu_particles_3d.h
+++ b/scene/3d/gpu_particles_3d.h
@@ -60,7 +60,10 @@ public:
private:
RID particles;
- bool one_shot;
+ bool emitting = false;
+ bool active = false;
+ bool signal_cancled = false;
+ bool one_shot = false;
int amount = 0;
double lifetime = 0.0;
double pre_process_time = 0.0;
@@ -87,6 +90,10 @@ private:
Vector<Ref<Mesh>> draw_passes;
Ref<Skin> skin;
+ double time = 0.0;
+ double emission_time = 0.0;
+ double active_time = 0.0;
+
void _attach_sub_emitter();
void _skinning_changed();
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp
index 810c0c5326..b0e7c73253 100644
--- a/scene/3d/label_3d.cpp
+++ b/scene/3d/label_3d.cpp
@@ -30,7 +30,6 @@
#include "label_3d.h"
-#include "core/core_string_names.h"
#include "scene/main/viewport.h"
#include "scene/resources/theme.h"
#include "scene/scene_string_names.h"
@@ -126,10 +125,6 @@ void Label3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &Label3D::generate_triangle_mesh);
- ClassDB::bind_method(D_METHOD("_queue_update"), &Label3D::_queue_update);
- ClassDB::bind_method(D_METHOD("_font_changed"), &Label3D::_font_changed);
- ClassDB::bind_method(D_METHOD("_im_update"), &Label3D::_im_update);
-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m"), "set_pixel_size", "get_pixel_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");
@@ -239,7 +234,7 @@ void Label3D::_queue_update() {
}
pending_update = true;
- call_deferred(SceneStringNames::get_singleton()->_im_update);
+ callable_mp(this, &Label3D::_im_update).call_deferred();
}
AABB Label3D::get_aabb() const {
@@ -766,12 +761,12 @@ void Label3D::_font_changed() {
void Label3D::set_font(const Ref<Font> &p_font) {
if (font_override != p_font) {
if (font_override.is_valid()) {
- font_override->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed"));
+ font_override->disconnect_changed(callable_mp(this, &Label3D::_font_changed));
}
font_override = p_font;
dirty_font = true;
if (font_override.is_valid()) {
- font_override->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed"));
+ font_override->connect_changed(callable_mp(this, &Label3D::_font_changed));
}
_queue_update();
}
@@ -783,7 +778,7 @@ Ref<Font> Label3D::get_font() const {
Ref<Font> Label3D::_get_font_or_default() const {
if (theme_font.is_valid()) {
- theme_font->disconnect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed"));
+ theme_font->disconnect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed));
theme_font.unref();
}
@@ -801,7 +796,7 @@ Ref<Font> Label3D::_get_font_or_default() const {
Ref<Font> f = ThemeDB::get_singleton()->get_project_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed"));
+ theme_font->connect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed));
}
return f;
}
@@ -818,7 +813,7 @@ Ref<Font> Label3D::_get_font_or_default() const {
Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E);
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed"));
+ theme_font->connect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed));
}
return f;
}
@@ -829,7 +824,7 @@ Ref<Font> Label3D::_get_font_or_default() const {
Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName());
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed"));
+ theme_font->connect_changed(callable_mp(const_cast<Label3D *>(this), &Label3D::_font_changed));
}
return f;
}
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 3ee08fd548..b4df06a83e 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -37,6 +37,7 @@
#include "scene/3d/mesh_instance_3d.h"
#include "scene/resources/camera_attributes.h"
#include "scene/resources/environment.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/sky.h"
void LightmapGIData::add_user(const NodePath &p_path, const Rect2 &p_uv_scale, int p_slice_index, int32_t p_sub_instance) {
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index 28a3cd0b13..0b0b098f65 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -31,7 +31,6 @@
#include "mesh_instance_3d.h"
#include "collision_shape_3d.h"
-#include "core/core_string_names.h"
#include "physics_body_3d.h"
#include "scene/resources/concave_polygon_shape_3d.h"
#include "scene/resources/convex_polygon_shape_3d.h"
@@ -111,7 +110,7 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) {
}
if (mesh.is_valid()) {
- mesh->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed));
+ mesh->disconnect_changed(callable_mp(this, &MeshInstance3D::_mesh_changed));
}
mesh = p_mesh;
@@ -120,7 +119,7 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) {
// If mesh is a PrimitiveMesh, calling get_rid on it can trigger a changed callback
// so do this before connecting _mesh_changed.
set_base(mesh->get_rid());
- mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed));
+ mesh->connect_changed(callable_mp(this, &MeshInstance3D::_mesh_changed));
_mesh_changed();
} else {
blend_shape_tracks.clear();
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 194d3082df..3f1cfae99d 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -30,7 +30,6 @@
#include "navigation_region_3d.h"
-#include "core/core_string_names.h"
#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
#include "servers/navigation_server_3d.h"
@@ -193,13 +192,13 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_naviga
}
if (navigation_mesh.is_valid()) {
- navigation_mesh->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
+ navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
}
navigation_mesh = p_navigation_mesh;
if (navigation_mesh.is_valid()) {
- navigation_mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
+ navigation_mesh->connect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
}
NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, p_navigation_mesh);
@@ -262,11 +261,19 @@ void _bake_navigation_mesh(void *p_user_data) {
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data = args->source_geometry_data;
NavigationServer3D::get_singleton()->bake_from_source_geometry_data(nav_mesh, source_geometry_data);
- args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh);
+ if (!Thread::is_main_thread()) {
+ args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh);
+ } else {
+ args->nav_region->_bake_finished(nav_mesh);
+ }
memdelete(args);
} else {
ERR_PRINT("Can't bake the navigation mesh if the `NavigationMesh` resource doesn't exist");
- args->nav_region->call_deferred(SNAME("_bake_finished"), Ref<NavigationMesh>());
+ if (!Thread::is_main_thread()) {
+ args->nav_region->call_deferred(SNAME("_bake_finished"), Ref<NavigationMesh>());
+ } else {
+ args->nav_region->_bake_finished(Ref<NavigationMesh>());
+ }
memdelete(args);
}
}
@@ -462,7 +469,7 @@ NavigationRegion3D::~NavigationRegion3D() {
}
if (navigation_mesh.is_valid()) {
- navigation_mesh->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
+ navigation_mesh->disconnect_changed(callable_mp(this, &NavigationRegion3D::_navigation_mesh_changed));
}
ERR_FAIL_NULL(NavigationServer3D::get_singleton());
NavigationServer3D::get_singleton()->free(region);
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index 8fd1df372b..7b535f6169 100644
--- a/scene/3d/occluder_instance_3d.cpp
+++ b/scene/3d/occluder_instance_3d.cpp
@@ -31,7 +31,6 @@
#include "occluder_instance_3d.h"
#include "core/config/project_settings.h"
-#include "core/core_string_names.h"
#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/math/triangulate.h"
@@ -441,14 +440,14 @@ void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) {
}
if (occluder.is_valid()) {
- occluder->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &OccluderInstance3D::_occluder_changed));
+ occluder->disconnect_changed(callable_mp(this, &OccluderInstance3D::_occluder_changed));
}
occluder = p_occluder;
if (occluder.is_valid()) {
set_base(occluder->get_rid());
- occluder->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &OccluderInstance3D::_occluder_changed));
+ occluder->connect_changed(callable_mp(this, &OccluderInstance3D::_occluder_changed));
} else {
set_base(RID());
}
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index 9516973ae2..6aea063096 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -144,13 +144,13 @@ void Path3D::_curve_changed() {
void Path3D::set_curve(const Ref<Curve3D> &p_curve) {
if (curve.is_valid()) {
- curve->disconnect("changed", callable_mp(this, &Path3D::_curve_changed));
+ curve->disconnect_changed(callable_mp(this, &Path3D::_curve_changed));
}
curve = p_curve;
if (curve.is_valid()) {
- curve->connect("changed", callable_mp(this, &Path3D::_curve_changed));
+ curve->connect_changed(callable_mp(this, &Path3D::_curve_changed));
}
_curve_changed();
}
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 6b31aa0a3c..e49b43c650 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -30,7 +30,6 @@
#include "physics_body_3d.h"
-#include "core/core_string_names.h"
#include "scene/scene_string_names.h"
void PhysicsBody3D::_bind_methods() {
@@ -214,15 +213,13 @@ real_t PhysicsBody3D::get_inverse_mass() const {
void StaticBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
if (physics_material_override.is_valid()) {
- if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody3D::_reload_physics_characteristics))) {
- physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody3D::_reload_physics_characteristics));
- }
+ physics_material_override->disconnect_changed(callable_mp(this, &StaticBody3D::_reload_physics_characteristics));
}
physics_material_override = p_physics_material_override;
if (physics_material_override.is_valid()) {
- physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &StaticBody3D::_reload_physics_characteristics));
+ physics_material_override->connect_changed(callable_mp(this, &StaticBody3D::_reload_physics_characteristics));
}
_reload_physics_characteristics();
}
@@ -726,15 +723,13 @@ const Vector3 &RigidBody3D::get_center_of_mass() const {
void RigidBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
if (physics_material_override.is_valid()) {
- if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics))) {
- physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics));
- }
+ physics_material_override->disconnect_changed(callable_mp(this, &RigidBody3D::_reload_physics_characteristics));
}
physics_material_override = p_physics_material_override;
if (physics_material_override.is_valid()) {
- physics_material_override->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics));
+ physics_material_override->connect_changed(callable_mp(this, &RigidBody3D::_reload_physics_characteristics));
}
_reload_physics_characteristics();
}
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index 2dea6418ed..7b04669dad 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -223,6 +223,7 @@ void RayCast3D::_update_raycast_state() {
ray_params.collide_with_bodies = collide_with_bodies;
ray_params.collide_with_areas = collide_with_areas;
ray_params.hit_from_inside = hit_from_inside;
+ ray_params.hit_back_faces = hit_back_faces;
PhysicsDirectSpaceState3D::RayResult rr;
if (dss->intersect_ray(ray_params, rr)) {
@@ -297,6 +298,14 @@ bool RayCast3D::is_hit_from_inside_enabled() const {
return hit_from_inside;
}
+void RayCast3D::set_hit_back_faces(bool p_enabled) {
+ hit_back_faces = p_enabled;
+}
+
+bool RayCast3D::is_hit_back_faces_enabled() const {
+ return hit_back_faces;
+}
+
void RayCast3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &RayCast3D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &RayCast3D::is_enabled);
@@ -339,6 +348,9 @@ void RayCast3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hit_from_inside", "enable"), &RayCast3D::set_hit_from_inside);
ClassDB::bind_method(D_METHOD("is_hit_from_inside_enabled"), &RayCast3D::is_hit_from_inside_enabled);
+ ClassDB::bind_method(D_METHOD("set_hit_back_faces", "enable"), &RayCast3D::set_hit_back_faces);
+ ClassDB::bind_method(D_METHOD("is_hit_back_faces_enabled"), &RayCast3D::is_hit_back_faces_enabled);
+
ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color", "debug_shape_custom_color"), &RayCast3D::set_debug_shape_custom_color);
ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color"), &RayCast3D::get_debug_shape_custom_color);
@@ -350,6 +362,7 @@ void RayCast3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position", PROPERTY_HINT_NONE, "suffix:m"), "set_target_position", "get_target_position");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hit_from_inside"), "set_hit_from_inside", "is_hit_from_inside_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hit_back_faces"), "set_hit_back_faces", "is_hit_back_faces_enabled");
ADD_GROUP("Collide With", "collide_with");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled");
diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h
index e230d61628..1def7a0eca 100644
--- a/scene/3d/ray_cast_3d.h
+++ b/scene/3d/ray_cast_3d.h
@@ -69,6 +69,7 @@ class RayCast3D : public Node3D {
bool collide_with_bodies = true;
bool hit_from_inside = false;
+ bool hit_back_faces = true;
protected:
void _notification(int p_what);
@@ -85,6 +86,9 @@ public:
void set_hit_from_inside(bool p_enabled);
bool is_hit_from_inside_enabled() const;
+ void set_hit_back_faces(bool p_enabled);
+ bool is_hit_back_faces_enabled() const;
+
void set_enabled(bool p_enabled);
bool is_enabled() const;
diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp
index 75f94b36d3..b6401832ed 100644
--- a/scene/3d/shape_cast_3d.cpp
+++ b/scene/3d/shape_cast_3d.cpp
@@ -30,7 +30,6 @@
#include "shape_cast_3d.h"
-#include "core/core_string_names.h"
#include "scene/3d/collision_object_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/resources/concave_polygon_shape_3d.h"
@@ -93,7 +92,9 @@ void ShapeCast3D::_notification(int p_what) {
}
void ShapeCast3D::_bind_methods() {
+#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &ShapeCast3D::resource_changed);
+#endif
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &ShapeCast3D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &ShapeCast3D::is_enabled);
@@ -312,12 +313,10 @@ real_t ShapeCast3D::get_closest_collision_unsafe_fraction() const {
return collision_unsafe_fraction;
}
+#ifndef DISABLE_DEPRECATED
void ShapeCast3D::resource_changed(Ref<Resource> p_res) {
- if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) {
- _update_debug_shape();
- }
- update_gizmos();
}
+#endif
void ShapeCast3D::_shape_changed() {
update_gizmos();
@@ -332,13 +331,11 @@ void ShapeCast3D::set_shape(const Ref<Shape3D> &p_shape) {
return;
}
if (shape.is_valid()) {
- shape->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast3D::_shape_changed));
- shape->unregister_owner(this);
+ shape->disconnect_changed(callable_mp(this, &ShapeCast3D::_shape_changed));
}
shape = p_shape;
if (shape.is_valid()) {
- shape->register_owner(this);
- shape->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast3D::_shape_changed));
+ shape->connect_changed(callable_mp(this, &ShapeCast3D::_shape_changed));
shape_rid = shape->get_rid();
}
@@ -633,9 +630,3 @@ void ShapeCast3D::_clear_debug_shape() {
debug_shape = nullptr;
}
-
-ShapeCast3D::~ShapeCast3D() {
- if (!shape.is_null()) {
- shape->unregister_owner(this);
- }
-}
diff --git a/scene/3d/shape_cast_3d.h b/scene/3d/shape_cast_3d.h
index 98158d3c7c..043e35090f 100644
--- a/scene/3d/shape_cast_3d.h
+++ b/scene/3d/shape_cast_3d.h
@@ -40,7 +40,9 @@ class ShapeCast3D : public Node3D {
GDCLASS(ShapeCast3D, Node3D);
bool enabled = true;
+#ifndef DISABLE_DEPRECATED
void resource_changed(Ref<Resource> p_res);
+#endif
Ref<Shape3D> shape;
RID shape_rid;
@@ -74,8 +76,6 @@ class ShapeCast3D : public Node3D {
Array _get_collision_result() const;
- ~ShapeCast3D();
-
protected:
void _notification(int p_what);
void _update_shapecast_state();
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 51359059ef..445c1003b5 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -887,7 +887,7 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
skin_bindings.insert(skin_ref.operator->());
- skin_ref->skin->connect("changed", callable_mp(skin_ref.operator->(), &SkinReference::_skin_changed));
+ skin_ref->skin->connect_changed(callable_mp(skin_ref.operator->(), &SkinReference::_skin_changed));
_make_dirty(); // Skin needs to be updated, so update skeleton.
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 6e44696fe4..1345cfcdb8 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -30,6 +30,7 @@
#include "sprite_3d.h"
+#include "scene/resources/atlas_texture.h"
#include "scene/scene_string_names.h"
Color SpriteBase3D::_get_color_accum() {
@@ -372,7 +373,7 @@ void SpriteBase3D::_queue_redraw() {
update_gizmos();
pending_update = true;
- call_deferred(SceneStringNames::get_singleton()->_im_update);
+ callable_mp(this, &SpriteBase3D::_im_update).call_deferred();
}
AABB SpriteBase3D::get_aabb() const {
@@ -577,8 +578,6 @@ void SpriteBase3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect);
ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh);
- ClassDB::bind_method(D_METHOD("_im_update"), &SpriteBase3D::_im_update);
-
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h");
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 6a76b5ac16..412017bc5a 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -1142,7 +1142,7 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), CONNECT_REFERENCE_COUNTED);
p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
- p_node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED);
+ p_node->connect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED);
}
Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) const {
@@ -1206,7 +1206,7 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed));
node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed));
node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed));
- node->disconnect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed));
+ node->disconnect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed));
}
nodes.erase(p_name);
@@ -1231,7 +1231,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN
ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output);
ERR_FAIL_COND(p_new_name == SceneStringNames::get_singleton()->output);
- nodes[p_name].node->disconnect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed));
+ nodes[p_name].node->disconnect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed));
nodes[p_new_name] = nodes[p_name];
nodes.erase(p_name);
@@ -1245,7 +1245,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN
}
}
// Connection must be done with new name.
- nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED);
+ nodes[p_new_name].node->connect_changed(callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED);
emit_signal(SNAME("animation_node_renamed"), get_instance_id(), p_name, p_new_name);
emit_signal(SNAME("tree_changed"));
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 5804d3250d..430569432a 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -30,7 +30,6 @@
#include "button.h"
-#include "core/core_string_names.h"
#include "core/string/translation.h"
#include "servers/rendering_server.h"
@@ -327,11 +326,8 @@ void Button::_notification(int p_what) {
if (align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER && icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
icon_ofs.x = 0.0;
}
- int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
- text_buf->set_width((clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? text_clip : -1);
-
- int text_width = MAX(1, (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x);
+ int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
if (_internal_margin[SIDE_LEFT] > 0) {
text_clip -= _internal_margin[SIDE_LEFT] + theme_cache.h_separation;
}
@@ -339,6 +335,10 @@ void Button::_notification(int p_what) {
text_clip -= _internal_margin[SIDE_RIGHT] + theme_cache.h_separation;
}
+ text_buf->set_width((clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? text_clip : -1);
+
+ int text_width = MAX(1, (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x);
+
Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0;
if (vertical_icon_alignment == VERTICAL_ALIGNMENT_TOP) {
@@ -539,13 +539,13 @@ void Button::set_icon(const Ref<Texture2D> &p_icon) {
}
if (icon.is_valid()) {
- icon->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Button::_texture_changed));
+ icon->disconnect_changed(callable_mp(this, &Button::_texture_changed));
}
icon = p_icon;
if (icon.is_valid()) {
- icon->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Button::_texture_changed));
+ icon->connect_changed(callable_mp(this, &Button::_texture_changed));
}
queue_redraw();
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 1493e4a79f..e50a1c0e88 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -36,6 +36,9 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "scene/gui/color_mode.h"
+#include "scene/resources/image_texture.h"
+#include "scene/resources/style_box_flat.h"
+#include "scene/resources/style_box_texture.h"
#include "servers/display_server.h"
#include "thirdparty/misc/ok_color.h"
#include "thirdparty/misc/ok_color_shader.h"
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 03e47cb5c3..1a4b1b6ee1 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -46,6 +46,7 @@
#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/texture_rect.h"
+#include "scene/resources/style_box_flat.h"
class ColorMode;
class ColorModeRGB;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 78862364d5..0ca04faf9b 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -271,21 +271,21 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
if (name.begins_with("theme_override_icons/")) {
String dname = name.get_slicec('/', 1);
if (data.theme_icon_override.has(dname)) {
- data.theme_icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ data.theme_icon_override[dname]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
data.theme_icon_override.erase(dname);
_notify_theme_override_changed();
} else if (name.begins_with("theme_override_styles/")) {
String dname = name.get_slicec('/', 1);
if (data.theme_style_override.has(dname)) {
- data.theme_style_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ data.theme_style_override[dname]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
data.theme_style_override.erase(dname);
_notify_theme_override_changed();
} else if (name.begins_with("theme_override_fonts/")) {
String dname = name.get_slicec('/', 1);
if (data.theme_font_override.has(dname)) {
- data.theme_font_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ data.theme_font_override[dname]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
data.theme_font_override.erase(dname);
_notify_theme_override_changed();
@@ -2468,13 +2468,13 @@ void Control::set_theme(const Ref<Theme> &p_theme) {
}
if (data.theme.is_valid()) {
- data.theme->disconnect("changed", callable_mp(this, &Control::_theme_changed));
+ data.theme->disconnect_changed(callable_mp(this, &Control::_theme_changed));
}
data.theme = p_theme;
if (data.theme.is_valid()) {
data.theme_owner->propagate_theme_changed(this, this, is_inside_tree(), true);
- data.theme->connect("changed", callable_mp(this, &Control::_theme_changed), CONNECT_DEFERRED);
+ data.theme->connect_changed(callable_mp(this, &Control::_theme_changed), CONNECT_DEFERRED);
return;
}
@@ -2769,11 +2769,11 @@ void Control::add_theme_icon_override(const StringName &p_name, const Ref<Textur
ERR_FAIL_COND(!p_icon.is_valid());
if (data.theme_icon_override.has(p_name)) {
- data.theme_icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ data.theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
data.theme_icon_override[p_name] = p_icon;
- data.theme_icon_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
+ data.theme_icon_override[p_name]->connect_changed(callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
_notify_theme_override_changed();
}
@@ -2782,11 +2782,11 @@ void Control::add_theme_style_override(const StringName &p_name, const Ref<Style
ERR_FAIL_COND(!p_style.is_valid());
if (data.theme_style_override.has(p_name)) {
- data.theme_style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ data.theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
data.theme_style_override[p_name] = p_style;
- data.theme_style_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
+ data.theme_style_override[p_name]->connect_changed(callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
_notify_theme_override_changed();
}
@@ -2795,11 +2795,11 @@ void Control::add_theme_font_override(const StringName &p_name, const Ref<Font>
ERR_FAIL_COND(!p_font.is_valid());
if (data.theme_font_override.has(p_name)) {
- data.theme_font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ data.theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
data.theme_font_override[p_name] = p_font;
- data.theme_font_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
+ data.theme_font_override[p_name]->connect_changed(callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
_notify_theme_override_changed();
}
@@ -2824,7 +2824,7 @@ void Control::add_theme_constant_override(const StringName &p_name, int p_consta
void Control::remove_theme_icon_override(const StringName &p_name) {
ERR_MAIN_THREAD_GUARD;
if (data.theme_icon_override.has(p_name)) {
- data.theme_icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ data.theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
data.theme_icon_override.erase(p_name);
@@ -2834,7 +2834,7 @@ void Control::remove_theme_icon_override(const StringName &p_name) {
void Control::remove_theme_style_override(const StringName &p_name) {
ERR_MAIN_THREAD_GUARD;
if (data.theme_style_override.has(p_name)) {
- data.theme_style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ data.theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
data.theme_style_override.erase(p_name);
@@ -2844,7 +2844,7 @@ void Control::remove_theme_style_override(const StringName &p_name) {
void Control::remove_theme_font_override(const StringName &p_name) {
ERR_MAIN_THREAD_GUARD;
if (data.theme_font_override.has(p_name)) {
- data.theme_font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ data.theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
data.theme_font_override.erase(p_name);
@@ -3630,13 +3630,13 @@ Control::~Control() {
// Resources need to be disconnected.
for (KeyValue<StringName, Ref<Texture2D>> &E : data.theme_icon_override) {
- E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ E.value->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
for (KeyValue<StringName, Ref<StyleBox>> &E : data.theme_style_override) {
- E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ E.value->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
for (KeyValue<StringName, Ref<Font>> &E : data.theme_font_override) {
- E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
+ E.value->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
}
// Then override maps can be simply cleared.
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index f7507640c8..d4da4797eb 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -54,6 +54,38 @@ void FileDialog::_focus_file_text() {
}
}
+void FileDialog::popup(const Rect2i &p_rect) {
+ if (access == ACCESS_FILESYSTEM && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
+ DisplayServer::get_singleton()->file_dialog_show(get_title(), dir->get_text(), file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, callable_mp(this, &FileDialog::_native_dialog_cb));
+ } else {
+ ConfirmationDialog::popup(p_rect);
+ }
+}
+
+void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files) {
+ if (p_ok) {
+ if (p_files.size() > 0) {
+ String f = p_files[0];
+ if (mode == FILE_MODE_OPEN_FILES) {
+ emit_signal(SNAME("files_selected"), p_files);
+ } else {
+ if (mode == FILE_MODE_SAVE_FILE) {
+ emit_signal(SNAME("file_selected"), f);
+ } else if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) {
+ emit_signal(SNAME("file_selected"), f);
+ } else if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_DIR) {
+ emit_signal(SNAME("dir_selected"), f);
+ }
+ }
+ file->set_text(f);
+ dir->set_text(f.get_base_dir());
+ }
+ } else {
+ file->set_text("");
+ emit_signal(SNAME("canceled"));
+ }
+}
+
VBoxContainer *FileDialog::get_vbox() {
return vbox;
}
@@ -980,6 +1012,8 @@ void FileDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_root_subfolder"), &FileDialog::get_root_subfolder);
ClassDB::bind_method(D_METHOD("set_show_hidden_files", "show"), &FileDialog::set_show_hidden_files);
ClassDB::bind_method(D_METHOD("is_showing_hidden_files"), &FileDialog::is_showing_hidden_files);
+ ClassDB::bind_method(D_METHOD("set_use_native_dialog", "native"), &FileDialog::set_use_native_dialog);
+ ClassDB::bind_method(D_METHOD("get_use_native_dialog"), &FileDialog::get_use_native_dialog);
ClassDB::bind_method(D_METHOD("deselect_all"), &FileDialog::deselect_all);
ClassDB::bind_method(D_METHOD("invalidate"), &FileDialog::invalidate);
@@ -990,6 +1024,7 @@ void FileDialog::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "root_subfolder"), "set_root_subfolder", "get_root_subfolder");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "filters"), "set_filters", "get_filters");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_native_dialog"), "set_use_native_dialog", "get_use_native_dialog");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir", PROPERTY_HINT_DIR, "", PROPERTY_USAGE_NONE), "set_current_dir", "get_current_dir");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_file", PROPERTY_HINT_FILE, "*", PROPERTY_USAGE_NONE), "set_current_file", "get_current_file");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_path", "get_current_path");
@@ -1025,6 +1060,14 @@ void FileDialog::set_default_show_hidden_files(bool p_show) {
default_show_hidden_files = p_show;
}
+void FileDialog::set_use_native_dialog(bool p_native) {
+ use_native_dialog = p_native;
+}
+
+bool FileDialog::get_use_native_dialog() const {
+ return use_native_dialog;
+}
+
FileDialog::FileDialog() {
show_hidden_files = default_show_hidden_files;
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 87c41be98b..dece241ca9 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -105,6 +105,7 @@ private:
static bool default_show_hidden_files;
bool show_hidden_files = false;
+ bool use_native_dialog = false;
bool is_invalidating = false;
@@ -158,6 +159,8 @@ private:
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
+ void _native_dialog_cb(bool p_ok, const Vector<String> &p_files);
+
bool _is_open_should_be_disabled();
virtual void _post_popup() override;
@@ -169,6 +172,8 @@ protected:
static void _bind_methods();
//bind helpers
public:
+ virtual void popup(const Rect2i &p_rect = Rect2i()) override;
+
void popup_file_dialog();
void clear_filters();
void add_filter(const String &p_filter, const String &p_description = "");
@@ -191,6 +196,9 @@ public:
void set_mode_overrides_title(bool p_override);
bool is_mode_overriding_title() const;
+ void set_use_native_dialog(bool p_native);
+ bool get_use_native_dialog() const;
+
void set_file_mode(FileMode p_mode);
FileMode get_file_mode() const;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 2c37017fa1..0c9ed37046 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -36,6 +36,7 @@
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/view_panner.h"
+#include "scene/resources/style_box_flat.h"
constexpr int MINIMAP_OFFSET = 12;
constexpr int MINIMAP_PADDING = 5;
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 1b48b9165d..cbaf1c372e 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -31,7 +31,6 @@
#include "label.h"
#include "core/config/project_settings.h"
-#include "core/core_string_names.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
@@ -784,11 +783,11 @@ void Label::_invalidate() {
void Label::set_label_settings(const Ref<LabelSettings> &p_settings) {
if (settings != p_settings) {
if (settings.is_valid()) {
- settings->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate));
+ settings->disconnect_changed(callable_mp(this, &Label::_invalidate));
}
settings = p_settings;
if (settings.is_valid()) {
- settings->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate), CONNECT_REFERENCE_COUNTED);
+ settings->connect_changed(callable_mp(this, &Label::_invalidate), CONNECT_REFERENCE_COUNTED);
}
_invalidate();
}
diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp
index 68e2db7cfd..e2ae824e60 100644
--- a/scene/gui/nine_patch_rect.cpp
+++ b/scene/gui/nine_patch_rect.cpp
@@ -30,7 +30,6 @@
#include "nine_patch_rect.h"
-#include "core/core_string_names.h"
#include "scene/scene_string_names.h"
#include "servers/rendering_server.h"
@@ -101,13 +100,13 @@ void NinePatchRect::set_texture(const Ref<Texture2D> &p_tex) {
}
if (texture.is_valid()) {
- texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NinePatchRect::_texture_changed));
+ texture->disconnect_changed(callable_mp(this, &NinePatchRect::_texture_changed));
}
texture = p_tex;
if (texture.is_valid()) {
- texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NinePatchRect::_texture_changed));
+ texture->connect_changed(callable_mp(this, &NinePatchRect::_texture_changed));
}
queue_redraw();
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 71d64c8bff..8138a66f64 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -445,13 +445,11 @@ void OptionButton::_select_int(int p_which) {
void OptionButton::_refresh_size_cache() {
cache_refresh_pending = false;
- if (!fit_to_longest_item) {
- return;
- }
-
- _cached_size = Vector2();
- for (int i = 0; i < get_item_count(); i++) {
- _cached_size = _cached_size.max(get_minimum_size_for_text_and_icon(popup->get_item_xl_text(i), get_item_icon(i)));
+ if (fit_to_longest_item) {
+ _cached_size = Vector2();
+ for (int i = 0; i < get_item_count(); i++) {
+ _cached_size = _cached_size.max(get_minimum_size_for_text_and_icon(popup->get_item_xl_text(i), get_item_icon(i)));
+ }
}
update_minimum_size();
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index e2324f3fe5..40db8deaac 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -1968,7 +1968,7 @@ void PopupMenu::clear() {
void PopupMenu::_ref_shortcut(Ref<Shortcut> p_sc) {
if (!shortcut_refcount.has(p_sc)) {
shortcut_refcount[p_sc] = 1;
- p_sc->connect("changed", callable_mp(this, &PopupMenu::_shortcut_changed));
+ p_sc->connect_changed(callable_mp(this, &PopupMenu::_shortcut_changed));
} else {
shortcut_refcount[p_sc] += 1;
}
@@ -1978,7 +1978,7 @@ void PopupMenu::_unref_shortcut(Ref<Shortcut> p_sc) {
ERR_FAIL_COND(!shortcut_refcount.has(p_sc));
shortcut_refcount[p_sc]--;
if (shortcut_refcount[p_sc] == 0) {
- p_sc->disconnect("changed", callable_mp(this, &PopupMenu::_shortcut_changed));
+ p_sc->disconnect_changed(callable_mp(this, &PopupMenu::_shortcut_changed));
shortcut_refcount.erase(p_sc);
}
}
@@ -2308,7 +2308,7 @@ void PopupMenu::_bind_methods() {
ADD_SIGNAL(MethodInfo("menu_changed"));
}
-void PopupMenu::popup(const Rect2 &p_bounds) {
+void PopupMenu::popup(const Rect2i &p_bounds) {
moved = Vector2();
popup_time_msec = OS::get_singleton()->get_ticks_msec();
Popup::popup(p_bounds);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index b4655f13ae..5ad9cd4303 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -312,7 +312,7 @@ public:
void set_allow_search(bool p_allow);
bool get_allow_search() const;
- virtual void popup(const Rect2 &p_bounds = Rect2());
+ virtual void popup(const Rect2i &p_bounds = Rect2i()) override;
void take_mouse_focus();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 37cb1d2dba..7cd346056a 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -35,7 +35,8 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/string/translation.h"
-#include "label.h"
+#include "scene/gui/label.h"
+#include "scene/resources/atlas_texture.h"
#include "scene/scene_string_names.h"
#include "servers/display_server.h"
@@ -3454,6 +3455,7 @@ void RichTextLabel::push_fade(int p_start_index, int p_length) {
_stop_thread();
MutexLock data_lock(data_mutex);
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemFade *item = memnew(ItemFade);
item->starting_index = p_start_index;
item->length = p_length;
@@ -3464,6 +3466,7 @@ void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f, bool p
_stop_thread();
MutexLock data_lock(data_mutex);
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemShake *item = memnew(ItemShake);
item->strength = p_strength;
item->rate = p_rate;
@@ -3475,6 +3478,7 @@ void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0
_stop_thread();
MutexLock data_lock(data_mutex);
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemWave *item = memnew(ItemWave);
item->frequency = p_frequency;
item->amplitude = p_amplitude;
@@ -3486,6 +3490,7 @@ void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0
_stop_thread();
MutexLock data_lock(data_mutex);
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemTornado *item = memnew(ItemTornado);
item->frequency = p_frequency;
item->radius = p_radius;
@@ -3497,6 +3502,7 @@ void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_freq
_stop_thread();
MutexLock data_lock(data_mutex);
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemRainbow *item = memnew(ItemRainbow);
item->frequency = p_frequency;
item->saturation = p_saturation;
@@ -3541,6 +3547,7 @@ void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionar
_stop_thread();
MutexLock data_lock(data_mutex);
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemCustomFX *item = memnew(ItemCustomFX);
item->custom_effect = p_custom_effect;
item->char_fx_transform->environment = p_environment;
@@ -3549,6 +3556,15 @@ void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionar
set_process_internal(true);
}
+void RichTextLabel::push_context() {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
+ ERR_FAIL_COND(current->type == ITEM_TABLE);
+ ItemContext *item = memnew(ItemContext);
+ _add_item(item, true);
+}
+
void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) {
_stop_thread();
MutexLock data_lock(data_mutex);
@@ -3642,6 +3658,31 @@ void RichTextLabel::pop() {
current = current->parent;
}
+void RichTextLabel::pop_context() {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
+ ERR_FAIL_NULL(current->parent);
+
+ while (current->parent && current != main) {
+ if (current->type == ITEM_FRAME) {
+ current_frame = static_cast<ItemFrame *>(current)->parent_frame;
+ } else if (current->type == ITEM_CONTEXT) {
+ current = current->parent;
+ return;
+ }
+ current = current->parent;
+ }
+}
+
+void RichTextLabel::pop_all() {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
+ current = main;
+ current_frame = main;
+}
+
void RichTextLabel::clear() {
_stop_thread();
MutexLock data_lock(data_mutex);
@@ -5551,7 +5592,10 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("push_fgcolor", "fgcolor"), &RichTextLabel::push_fgcolor);
ClassDB::bind_method(D_METHOD("push_bgcolor", "bgcolor"), &RichTextLabel::push_bgcolor);
ClassDB::bind_method(D_METHOD("push_customfx", "effect", "env"), &RichTextLabel::push_customfx);
+ ClassDB::bind_method(D_METHOD("push_context"), &RichTextLabel::push_context);
+ ClassDB::bind_method(D_METHOD("pop_context"), &RichTextLabel::pop_context);
ClassDB::bind_method(D_METHOD("pop"), &RichTextLabel::pop);
+ ClassDB::bind_method(D_METHOD("pop_all"), &RichTextLabel::pop_all);
ClassDB::bind_method(D_METHOD("clear"), &RichTextLabel::clear);
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 1c543c625a..30adf03f84 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -32,8 +32,8 @@
#define RICH_TEXT_LABEL_H
#include "core/object/worker_thread_pool.h"
-#include "rich_text_effect.h"
#include "scene/gui/popup_menu.h"
+#include "scene/gui/rich_text_effect.h"
#include "scene/gui/scroll_bar.h"
#include "scene/resources/text_paragraph.h"
@@ -76,7 +76,8 @@ public:
ITEM_META,
ITEM_HINT,
ITEM_DROPCAP,
- ITEM_CUSTOMFX
+ ITEM_CUSTOMFX,
+ ITEM_CONTEXT
};
enum MenuItems {
@@ -379,6 +380,10 @@ private:
}
};
+ struct ItemContext : public Item {
+ ItemContext() { type = ITEM_CONTEXT; }
+ };
+
ItemFrame *main = nullptr;
Item *current = nullptr;
ItemFrame *current_frame = nullptr;
@@ -624,6 +629,7 @@ public:
void push_bgcolor(const Color &p_color);
void push_fgcolor(const Color &p_color);
void push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment);
+ void push_context();
void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1);
void set_cell_row_background_color(const Color &p_odd_row_bg, const Color &p_even_row_bg);
void set_cell_border_color(const Color &p_color);
@@ -632,6 +638,8 @@ public:
int get_current_table_column() const;
void push_cell();
void pop();
+ void pop_context();
+ void pop_all();
void clear();
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index b07a3d7669..dcbb25c41d 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -30,7 +30,6 @@
#include "texture_button.h"
-#include "core/core_string_names.h"
#include "core/typedefs.h"
#include <stdlib.h>
@@ -353,12 +352,12 @@ void TextureButton::_set_texture(Ref<Texture2D> *p_destination, const Ref<Textur
return;
}
if (destination.is_valid()) {
- destination->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureButton::_texture_changed));
+ destination->disconnect_changed(callable_mp(this, &TextureButton::_texture_changed));
}
destination = p_texture;
if (destination.is_valid()) {
// Pass `CONNECT_REFERENCE_COUNTED` to avoid early disconnect in case the same texture is assigned to different "slots".
- destination->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureButton::_texture_changed), CONNECT_REFERENCE_COUNTED);
+ destination->connect_changed(callable_mp(this, &TextureButton::_texture_changed), CONNECT_REFERENCE_COUNTED);
}
_texture_changed();
}
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index 2464e005ee..70c2cc9d65 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -31,7 +31,7 @@
#include "texture_progress_bar.h"
#include "core/config/engine.h"
-#include "core/core_string_names.h"
+#include "scene/resources/atlas_texture.h"
void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) {
_set_texture(&under, p_texture);
@@ -161,12 +161,12 @@ void TextureProgressBar::_set_texture(Ref<Texture2D> *p_destination, const Ref<T
return;
}
if (destination.is_valid()) {
- destination->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureProgressBar::_texture_changed));
+ destination->disconnect_changed(callable_mp(this, &TextureProgressBar::_texture_changed));
}
destination = p_texture;
if (destination.is_valid()) {
// Pass `CONNECT_REFERENCE_COUNTED` to avoid early disconnect in case the same texture is assigned to different "slots".
- destination->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureProgressBar::_texture_changed), CONNECT_REFERENCE_COUNTED);
+ destination->connect_changed(callable_mp(this, &TextureProgressBar::_texture_changed), CONNECT_REFERENCE_COUNTED);
}
_texture_changed();
}
diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp
index 20472ab46e..d94b11789f 100644
--- a/scene/gui/texture_rect.cpp
+++ b/scene/gui/texture_rect.cpp
@@ -30,7 +30,7 @@
#include "texture_rect.h"
-#include "core/core_string_names.h"
+#include "scene/resources/atlas_texture.h"
#include "servers/rendering_server.h"
void TextureRect::_notification(int p_what) {
@@ -201,13 +201,13 @@ void TextureRect::set_texture(const Ref<Texture2D> &p_tex) {
}
if (texture.is_valid()) {
- texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureRect::_texture_changed));
+ texture->disconnect_changed(callable_mp(this, &TextureRect::_texture_changed));
}
texture = p_tex;
if (texture.is_valid()) {
- texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TextureRect::_texture_changed));
+ texture->connect_changed(callable_mp(this, &TextureRect::_texture_changed));
}
queue_redraw();
diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp
index 49a24f217d..f6e558fe57 100644
--- a/scene/gui/video_stream_player.cpp
+++ b/scene/gui/video_stream_player.cpp
@@ -31,6 +31,7 @@
#include "video_stream_player.h"
#include "core/os/os.h"
+#include "scene/resources/image_texture.h"
#include "scene/scene_string_names.h"
#include "servers/audio_server.h"
diff --git a/scene/gui/video_stream_player.h b/scene/gui/video_stream_player.h
index 7a922b2d92..0b83608f0b 100644
--- a/scene/gui/video_stream_player.h
+++ b/scene/gui/video_stream_player.h
@@ -36,6 +36,8 @@
#include "servers/audio/audio_rb_resampler.h"
#include "servers/audio_server.h"
+class ImageTexture;
+
class VideoStreamPlayer : public Control {
GDCLASS(VideoStreamPlayer, Control);
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 6b18e47f2d..cb72e4ec08 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1139,7 +1139,6 @@ void Node::_set_name_nocheck(const StringName &p_name) {
void Node::set_name(const String &p_name) {
ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Changing the name to nodes inside the SceneTree is only allowed from the main thread. Use `set_name.call_deferred(new_name)`.");
- ERR_FAIL_COND_MSG(data.parent && data.parent->data.blocked > 0, "Parent node is busy setting up children, `set_name(new_name)` failed. Consider using `set_name.call_deferred(new_name)` instead.");
String name = p_name.validate_node_name();
ERR_FAIL_COND(name.is_empty());
@@ -1151,9 +1150,9 @@ void Node::set_name(const String &p_name) {
data.name = name;
if (data.parent) {
- data.parent->data.children.erase(old_name);
data.parent->_validate_child_name(this, true);
- data.parent->data.children.insert(data.name, this);
+ bool success = data.parent->data.children.replace_key(old_name, data.name);
+ ERR_FAIL_COND_MSG(!success, "Renaming child in hashtable failed, this is a bug.");
}
if (data.unique_name_in_owner && data.owner) {
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index fe5c8f2c1d..7da0e0c339 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -50,6 +50,7 @@
#include "scene/main/viewport.h"
#include "scene/resources/environment.h"
#include "scene/resources/font.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
#include "scene/resources/packed_scene.h"
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 71bef56ebb..6d2706db1e 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -31,7 +31,6 @@
#include "viewport.h"
#include "core/config/project_settings.h"
-#include "core/core_string_names.h"
#include "core/debugger/engine_debugger.h"
#include "core/object/message_queue.h"
#include "core/string/translation.h"
@@ -315,7 +314,7 @@ void Viewport::_sub_window_update(Window *p_window) {
Rect2i r = Rect2i(p_window->get_position(), sw.window->get_size());
if (!p_window->get_flag(Window::FLAG_BORDERLESS)) {
- Ref<StyleBox> panel = p_window->get_theme_stylebox(SNAME("embedded_border"));
+ Ref<StyleBox> panel = p_window->get_theme_stylebox(gui.subwindow_focused == p_window ? SNAME("embedded_border") : SNAME("embedded_unfocused_border"));
panel->draw(sw.canvas_item, r);
// Draw the title bar text.
@@ -3864,7 +3863,7 @@ void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) {
}
if (own_world_3d.is_valid() && world_3d.is_valid()) {
- world_3d->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_3d_changed));
+ world_3d->disconnect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
}
world_3d = p_world_3d;
@@ -3872,7 +3871,7 @@ void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) {
if (own_world_3d.is_valid()) {
if (world_3d.is_valid()) {
own_world_3d = world_3d->duplicate();
- world_3d->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_3d_changed));
+ world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
} else {
own_world_3d = Ref<World3D>(memnew(World3D));
}
@@ -3923,14 +3922,14 @@ void Viewport::set_use_own_world_3d(bool p_use_own_world_3d) {
if (p_use_own_world_3d) {
if (world_3d.is_valid()) {
own_world_3d = world_3d->duplicate();
- world_3d->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_3d_changed));
+ world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
} else {
own_world_3d = Ref<World3D>(memnew(World3D));
}
} else {
own_world_3d = Ref<World3D>();
if (world_3d.is_valid()) {
- world_3d->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Viewport::_own_world_3d_changed));
+ world_3d->disconnect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
}
}
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index be88b757fd..3ea53da141 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -54,21 +54,21 @@ bool Window::_set(const StringName &p_name, const Variant &p_value) {
if (name.begins_with("theme_override_icons/")) {
String dname = name.get_slicec('/', 1);
if (theme_icon_override.has(dname)) {
- theme_icon_override[dname]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ theme_icon_override[dname]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
theme_icon_override.erase(dname);
_notify_theme_override_changed();
} else if (name.begins_with("theme_override_styles/")) {
String dname = name.get_slicec('/', 1);
if (theme_style_override.has(dname)) {
- theme_style_override[dname]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ theme_style_override[dname]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
theme_style_override.erase(dname);
_notify_theme_override_changed();
} else if (name.begins_with("theme_override_fonts/")) {
String dname = name.get_slicec('/', 1);
if (theme_font_override.has(dname)) {
- theme_font_override[dname]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ theme_font_override[dname]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
theme_font_override.erase(dname);
_notify_theme_override_changed();
@@ -1823,13 +1823,13 @@ void Window::set_theme(const Ref<Theme> &p_theme) {
}
if (theme.is_valid()) {
- theme->disconnect("changed", callable_mp(this, &Window::_theme_changed));
+ theme->disconnect_changed(callable_mp(this, &Window::_theme_changed));
}
theme = p_theme;
if (theme.is_valid()) {
theme_owner->propagate_theme_changed(this, this, is_inside_tree(), true);
- theme->connect("changed", callable_mp(this, &Window::_theme_changed), CONNECT_DEFERRED);
+ theme->connect_changed(callable_mp(this, &Window::_theme_changed), CONNECT_DEFERRED);
return;
}
@@ -2165,11 +2165,11 @@ void Window::add_theme_icon_override(const StringName &p_name, const Ref<Texture
ERR_FAIL_COND(!p_icon.is_valid());
if (theme_icon_override.has(p_name)) {
- theme_icon_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
theme_icon_override[p_name] = p_icon;
- theme_icon_override[p_name]->connect("changed", callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
+ theme_icon_override[p_name]->connect_changed(callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
_notify_theme_override_changed();
}
@@ -2178,11 +2178,11 @@ void Window::add_theme_style_override(const StringName &p_name, const Ref<StyleB
ERR_FAIL_COND(!p_style.is_valid());
if (theme_style_override.has(p_name)) {
- theme_style_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
theme_style_override[p_name] = p_style;
- theme_style_override[p_name]->connect("changed", callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
+ theme_style_override[p_name]->connect_changed(callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
_notify_theme_override_changed();
}
@@ -2191,11 +2191,11 @@ void Window::add_theme_font_override(const StringName &p_name, const Ref<Font> &
ERR_FAIL_COND(!p_font.is_valid());
if (theme_font_override.has(p_name)) {
- theme_font_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
theme_font_override[p_name] = p_font;
- theme_font_override[p_name]->connect("changed", callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
+ theme_font_override[p_name]->connect_changed(callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
_notify_theme_override_changed();
}
@@ -2220,7 +2220,7 @@ void Window::add_theme_constant_override(const StringName &p_name, int p_constan
void Window::remove_theme_icon_override(const StringName &p_name) {
ERR_MAIN_THREAD_GUARD;
if (theme_icon_override.has(p_name)) {
- theme_icon_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
theme_icon_override.erase(p_name);
@@ -2230,7 +2230,7 @@ void Window::remove_theme_icon_override(const StringName &p_name) {
void Window::remove_theme_style_override(const StringName &p_name) {
ERR_MAIN_THREAD_GUARD;
if (theme_style_override.has(p_name)) {
- theme_style_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
theme_style_override.erase(p_name);
@@ -2240,7 +2240,7 @@ void Window::remove_theme_style_override(const StringName &p_name) {
void Window::remove_theme_font_override(const StringName &p_name) {
ERR_MAIN_THREAD_GUARD;
if (theme_font_override.has(p_name)) {
- theme_font_override[p_name]->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
theme_font_override.erase(p_name);
@@ -2768,13 +2768,13 @@ Window::~Window() {
// Resources need to be disconnected.
for (KeyValue<StringName, Ref<Texture2D>> &E : theme_icon_override) {
- E.value->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ E.value->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
for (KeyValue<StringName, Ref<StyleBox>> &E : theme_style_override) {
- E.value->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ E.value->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
for (KeyValue<StringName, Ref<Font>> &E : theme_font_override) {
- E.value->disconnect("changed", callable_mp(this, &Window::_notify_theme_override_changed));
+ E.value->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
}
// Then override maps can be simply cleared.
diff --git a/scene/main/window.h b/scene/main/window.h
index b98b888380..7a10499d9b 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -315,7 +315,7 @@ public:
Window *get_parent_visible_window() const;
Viewport *get_parent_viewport() const;
- void popup(const Rect2i &p_screen_rect = Rect2i());
+ virtual void popup(const Rect2i &p_screen_rect = Rect2i());
void popup_on_parent(const Rect2i &p_parent_rect);
void popup_centered(const Size2i &p_minsize = Size2i());
void popup_centered_ratio(float p_ratio = 0.8);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 9706fb1a5d..1c2ed16e63 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -139,30 +139,38 @@
#include "scene/main/timer.h"
#include "scene/main/viewport.h"
#include "scene/main/window.h"
+#include "scene/resources/animated_texture.h"
#include "scene/resources/animation_library.h"
+#include "scene/resources/atlas_texture.h"
#include "scene/resources/audio_stream_polyphonic.h"
#include "scene/resources/audio_stream_wav.h"
#include "scene/resources/bit_map.h"
#include "scene/resources/bone_map.h"
#include "scene/resources/box_shape_3d.h"
#include "scene/resources/camera_attributes.h"
+#include "scene/resources/camera_texture.h"
#include "scene/resources/capsule_shape_2d.h"
#include "scene/resources/capsule_shape_3d.h"
#include "scene/resources/circle_shape_2d.h"
+#include "scene/resources/compressed_texture.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/concave_polygon_shape_3d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_3d.h"
+#include "scene/resources/curve_texture.h"
#include "scene/resources/cylinder_shape_3d.h"
#include "scene/resources/default_theme/default_theme.h"
#include "scene/resources/environment.h"
#include "scene/resources/font.h"
#include "scene/resources/gradient.h"
+#include "scene/resources/gradient_texture.h"
#include "scene/resources/height_map_shape_3d.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/immediate_mesh.h"
#include "scene/resources/label_settings.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh_data_tool.h"
+#include "scene/resources/mesh_texture.h"
#include "scene/resources/multimesh.h"
#include "scene/resources/navigation_mesh.h"
#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
@@ -170,7 +178,9 @@
#include "scene/resources/packed_scene.h"
#include "scene/resources/particle_process_material.h"
#include "scene/resources/physics_material.h"
+#include "scene/resources/placeholder_textures.h"
#include "scene/resources/polygon_path_finder.h"
+#include "scene/resources/portable_compressed_texture.h"
#include "scene/resources/primitive_meshes.h"
#include "scene/resources/rectangle_shape_2d.h"
#include "scene/resources/resource_format_text.h"
@@ -192,6 +202,9 @@
#include "scene/resources/sky_material.h"
#include "scene/resources/sphere_shape_3d.h"
#include "scene/resources/style_box.h"
+#include "scene/resources/style_box_flat.h"
+#include "scene/resources/style_box_line.h"
+#include "scene/resources/style_box_texture.h"
#include "scene/resources/surface_tool.h"
#include "scene/resources/syntax_highlighter.h"
#include "scene/resources/text_file.h"
diff --git a/scene/resources/animated_texture.cpp b/scene/resources/animated_texture.cpp
new file mode 100644
index 0000000000..842a398327
--- /dev/null
+++ b/scene/resources/animated_texture.cpp
@@ -0,0 +1,286 @@
+/**************************************************************************/
+/* animated_texture.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 "animated_texture.h"
+
+void AnimatedTexture::_update_proxy() {
+ RWLockRead r(rw_lock);
+
+ float delta;
+ if (prev_ticks == 0) {
+ delta = 0;
+ prev_ticks = OS::get_singleton()->get_ticks_usec();
+ } else {
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec();
+ delta = float(double(ticks - prev_ticks) / 1000000.0);
+ prev_ticks = ticks;
+ }
+
+ time += delta;
+
+ float speed = speed_scale == 0 ? 0 : abs(1.0 / speed_scale);
+
+ int iter_max = frame_count;
+ while (iter_max && !pause) {
+ float frame_limit = frames[current_frame].duration * speed;
+
+ if (time > frame_limit) {
+ if (speed_scale > 0.0) {
+ current_frame++;
+ } else {
+ current_frame--;
+ }
+ if (current_frame >= frame_count) {
+ if (one_shot) {
+ current_frame = frame_count - 1;
+ } else {
+ current_frame = 0;
+ }
+ } else if (current_frame < 0) {
+ if (one_shot) {
+ current_frame = 0;
+ } else {
+ current_frame = frame_count - 1;
+ }
+ }
+ time -= frame_limit;
+
+ } else {
+ break;
+ }
+ iter_max--;
+ }
+
+ if (frames[current_frame].texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_proxy_update(proxy, frames[current_frame].texture->get_rid());
+ }
+}
+
+void AnimatedTexture::set_frames(int p_frames) {
+ ERR_FAIL_COND(p_frames < 1 || p_frames > MAX_FRAMES);
+
+ RWLockWrite r(rw_lock);
+
+ frame_count = p_frames;
+}
+
+int AnimatedTexture::get_frames() const {
+ return frame_count;
+}
+
+void AnimatedTexture::set_current_frame(int p_frame) {
+ ERR_FAIL_COND(p_frame < 0 || p_frame >= frame_count);
+
+ RWLockWrite r(rw_lock);
+
+ current_frame = p_frame;
+ time = 0;
+}
+
+int AnimatedTexture::get_current_frame() const {
+ return current_frame;
+}
+
+void AnimatedTexture::set_pause(bool p_pause) {
+ RWLockWrite r(rw_lock);
+ pause = p_pause;
+}
+
+bool AnimatedTexture::get_pause() const {
+ return pause;
+}
+
+void AnimatedTexture::set_one_shot(bool p_one_shot) {
+ RWLockWrite r(rw_lock);
+ one_shot = p_one_shot;
+}
+
+bool AnimatedTexture::get_one_shot() const {
+ return one_shot;
+}
+
+void AnimatedTexture::set_frame_texture(int p_frame, const Ref<Texture2D> &p_texture) {
+ ERR_FAIL_COND(p_texture == this);
+ ERR_FAIL_INDEX(p_frame, MAX_FRAMES);
+
+ RWLockWrite w(rw_lock);
+
+ frames[p_frame].texture = p_texture;
+}
+
+Ref<Texture2D> AnimatedTexture::get_frame_texture(int p_frame) const {
+ ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, Ref<Texture2D>());
+
+ RWLockRead r(rw_lock);
+
+ return frames[p_frame].texture;
+}
+
+void AnimatedTexture::set_frame_duration(int p_frame, float p_duration) {
+ ERR_FAIL_INDEX(p_frame, MAX_FRAMES);
+
+ RWLockWrite r(rw_lock);
+
+ frames[p_frame].duration = p_duration;
+}
+
+float AnimatedTexture::get_frame_duration(int p_frame) const {
+ ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, 0);
+
+ RWLockRead r(rw_lock);
+
+ return frames[p_frame].duration;
+}
+
+void AnimatedTexture::set_speed_scale(float p_scale) {
+ ERR_FAIL_COND(p_scale < -1000 || p_scale >= 1000);
+
+ RWLockWrite r(rw_lock);
+
+ speed_scale = p_scale;
+}
+
+float AnimatedTexture::get_speed_scale() const {
+ return speed_scale;
+}
+
+int AnimatedTexture::get_width() const {
+ RWLockRead r(rw_lock);
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return 1;
+ }
+
+ return frames[current_frame].texture->get_width();
+}
+
+int AnimatedTexture::get_height() const {
+ RWLockRead r(rw_lock);
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return 1;
+ }
+
+ return frames[current_frame].texture->get_height();
+}
+
+RID AnimatedTexture::get_rid() const {
+ return proxy;
+}
+
+bool AnimatedTexture::has_alpha() const {
+ RWLockRead r(rw_lock);
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return false;
+ }
+
+ return frames[current_frame].texture->has_alpha();
+}
+
+Ref<Image> AnimatedTexture::get_image() const {
+ RWLockRead r(rw_lock);
+
+ if (!frames[current_frame].texture.is_valid()) {
+ return Ref<Image>();
+ }
+
+ return frames[current_frame].texture->get_image();
+}
+
+bool AnimatedTexture::is_pixel_opaque(int p_x, int p_y) const {
+ RWLockRead r(rw_lock);
+
+ if (frames[current_frame].texture.is_valid()) {
+ return frames[current_frame].texture->is_pixel_opaque(p_x, p_y);
+ }
+ return true;
+}
+
+void AnimatedTexture::_validate_property(PropertyInfo &p_property) const {
+ String prop = p_property.name;
+ if (prop.begins_with("frame_")) {
+ int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int();
+ if (frame >= frame_count) {
+ p_property.usage = PROPERTY_USAGE_NONE;
+ }
+ }
+}
+
+void AnimatedTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_frames", "frames"), &AnimatedTexture::set_frames);
+ ClassDB::bind_method(D_METHOD("get_frames"), &AnimatedTexture::get_frames);
+
+ ClassDB::bind_method(D_METHOD("set_current_frame", "frame"), &AnimatedTexture::set_current_frame);
+ ClassDB::bind_method(D_METHOD("get_current_frame"), &AnimatedTexture::get_current_frame);
+
+ ClassDB::bind_method(D_METHOD("set_pause", "pause"), &AnimatedTexture::set_pause);
+ ClassDB::bind_method(D_METHOD("get_pause"), &AnimatedTexture::get_pause);
+
+ ClassDB::bind_method(D_METHOD("set_one_shot", "one_shot"), &AnimatedTexture::set_one_shot);
+ ClassDB::bind_method(D_METHOD("get_one_shot"), &AnimatedTexture::get_one_shot);
+
+ ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &AnimatedTexture::set_speed_scale);
+ ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedTexture::get_speed_scale);
+
+ ClassDB::bind_method(D_METHOD("set_frame_texture", "frame", "texture"), &AnimatedTexture::set_frame_texture);
+ ClassDB::bind_method(D_METHOD("get_frame_texture", "frame"), &AnimatedTexture::get_frame_texture);
+
+ ClassDB::bind_method(D_METHOD("set_frame_duration", "frame", "duration"), &AnimatedTexture::set_frame_duration);
+ ClassDB::bind_method(D_METHOD("get_frame_duration", "frame"), &AnimatedTexture::get_frame_duration);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "current_frame", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_frame", "get_current_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pause"), "set_pause", "get_pause");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-60,60,0.1,or_less,or_greater"), "set_speed_scale", "get_speed_scale");
+
+ for (int i = 0; i < MAX_FRAMES; i++) {
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_texture", "get_frame_texture", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "frame_" + itos(i) + "/duration", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_duration", "get_frame_duration", i);
+ }
+
+ BIND_CONSTANT(MAX_FRAMES);
+}
+
+AnimatedTexture::AnimatedTexture() {
+ //proxy = RS::get_singleton()->texture_create();
+ proxy_ph = RS::get_singleton()->texture_2d_placeholder_create();
+ proxy = RS::get_singleton()->texture_proxy_create(proxy_ph);
+
+ RenderingServer::get_singleton()->texture_set_force_redraw_if_visible(proxy, true);
+ RenderingServer::get_singleton()->connect("frame_pre_draw", callable_mp(this, &AnimatedTexture::_update_proxy));
+}
+
+AnimatedTexture::~AnimatedTexture() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(proxy);
+ RS::get_singleton()->free(proxy_ph);
+}
diff --git a/scene/resources/animated_texture.h b/scene/resources/animated_texture.h
new file mode 100644
index 0000000000..844959c810
--- /dev/null
+++ b/scene/resources/animated_texture.h
@@ -0,0 +1,109 @@
+/**************************************************************************/
+/* animated_texture.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 ANIMATED_TEXTURE_H
+#define ANIMATED_TEXTURE_H
+
+#include "scene/resources/texture.h"
+
+class AnimatedTexture : public Texture2D {
+ GDCLASS(AnimatedTexture, Texture2D);
+
+ // Use readers writers lock for this, since its far more times read than written to.
+ RWLock rw_lock;
+
+public:
+ enum {
+ MAX_FRAMES = 256
+ };
+
+private:
+ RID proxy_ph;
+ RID proxy;
+
+ struct Frame {
+ Ref<Texture2D> texture;
+ float duration = 1.0;
+ };
+
+ Frame frames[MAX_FRAMES];
+ int frame_count = 1.0;
+ int current_frame = 0;
+ bool pause = false;
+ bool one_shot = false;
+ float speed_scale = 1.0;
+
+ float time = 0.0;
+
+ uint64_t prev_ticks = 0;
+
+ void _update_proxy();
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const;
+
+public:
+ void set_frames(int p_frames);
+ int get_frames() const;
+
+ void set_current_frame(int p_frame);
+ int get_current_frame() const;
+
+ void set_pause(bool p_pause);
+ bool get_pause() const;
+
+ void set_one_shot(bool p_one_shot);
+ bool get_one_shot() const;
+
+ void set_frame_texture(int p_frame, const Ref<Texture2D> &p_texture);
+ Ref<Texture2D> get_frame_texture(int p_frame) const;
+
+ void set_frame_duration(int p_frame, float p_duration);
+ float get_frame_duration(int p_frame) const;
+
+ void set_speed_scale(float p_scale);
+ float get_speed_scale() const;
+
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual RID get_rid() const override;
+
+ virtual bool has_alpha() const override;
+
+ virtual Ref<Image> get_image() const override;
+
+ bool is_pixel_opaque(int p_x, int p_y) const override;
+
+ AnimatedTexture();
+ ~AnimatedTexture();
+};
+
+#endif // ANIMATED_TEXTURE_H
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 1ab6e714a8..860e48a361 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -5508,6 +5508,9 @@ Variant Animation::add_variant(const Variant &a, const Variant &b) {
const ::AABB ab = b.operator ::AABB();
return ::AABB(aa.position + ab.position, aa.size + ab.size);
}
+ case Variant::BASIS: {
+ return (a.operator Basis()) * (b.operator Basis());
+ }
case Variant::QUATERNION: {
return (a.operator Quaternion()) * (b.operator Quaternion());
}
@@ -5555,14 +5558,17 @@ Variant Animation::subtract_variant(const Variant &a, const Variant &b) {
const ::AABB ab = b.operator ::AABB();
return ::AABB(aa.position - ab.position, aa.size - ab.size);
}
+ case Variant::BASIS: {
+ return (b.operator Basis()).inverse() * (a.operator Basis());
+ }
case Variant::QUATERNION: {
return (b.operator Quaternion()).inverse() * (a.operator Quaternion());
}
case Variant::TRANSFORM2D: {
- return (b.operator Transform2D()).inverse() * (a.operator Transform2D());
+ return (b.operator Transform2D()).affine_inverse() * (a.operator Transform2D());
}
case Variant::TRANSFORM3D: {
- return (b.operator Transform3D()).inverse() * (a.operator Transform3D());
+ return (b.operator Transform3D()).affine_inverse() * (a.operator Transform3D());
}
default: {
return Variant::evaluate(Variant::OP_SUBTRACT, a, b);
diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp
index 206e9df71a..a69b1818d2 100644
--- a/scene/resources/animation_library.cpp
+++ b/scene/resources/animation_library.cpp
@@ -52,13 +52,13 @@ Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animat
ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER);
if (animations.has(p_name)) {
- animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed));
+ animations.get(p_name)->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed));
animations.erase(p_name);
emit_signal(SNAME("animation_removed"), p_name);
}
animations.insert(p_name, p_animation);
- animations.get(p_name)->connect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_name));
+ animations.get(p_name)->connect_changed(callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_name));
emit_signal(SNAME("animation_added"), p_name);
notify_property_list_changed();
return OK;
@@ -67,7 +67,7 @@ Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animat
void AnimationLibrary::remove_animation(const StringName &p_name) {
ERR_FAIL_COND_MSG(!animations.has(p_name), vformat("Animation not found: %s.", p_name));
- animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed));
+ animations.get(p_name)->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed));
animations.erase(p_name);
emit_signal(SNAME("animation_removed"), p_name);
notify_property_list_changed();
@@ -78,8 +78,8 @@ void AnimationLibrary::rename_animation(const StringName &p_name, const StringNa
ERR_FAIL_COND_MSG(!is_valid_animation_name(p_new_name), "Invalid animation name: '" + String(p_new_name) + "'.");
ERR_FAIL_COND_MSG(animations.has(p_new_name), vformat("Animation name \"%s\" already exists in library.", p_new_name));
- animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed));
- animations.get(p_name)->connect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_new_name));
+ animations.get(p_name)->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed));
+ animations.get(p_name)->connect_changed(callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_new_name));
animations.insert(p_new_name, animations[p_name]);
animations.erase(p_name);
emit_signal(SNAME("animation_renamed"), p_name, p_new_name);
@@ -125,7 +125,7 @@ void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const
void AnimationLibrary::_set_data(const Dictionary &p_data) {
for (KeyValue<StringName, Ref<Animation>> &K : animations) {
- K.value->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed));
+ K.value->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed));
}
animations.clear();
List<Variant> keys;
diff --git a/scene/resources/atlas_texture.cpp b/scene/resources/atlas_texture.cpp
new file mode 100644
index 0000000000..24bf25f2db
--- /dev/null
+++ b/scene/resources/atlas_texture.cpp
@@ -0,0 +1,255 @@
+/**************************************************************************/
+/* atlas_texture.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 "atlas_texture.h"
+
+#include "core/core_string_names.h"
+
+int AtlasTexture::get_width() const {
+ if (region.size.width == 0) {
+ if (atlas.is_valid()) {
+ return atlas->get_width();
+ }
+ return 1;
+ } else {
+ return region.size.width + margin.size.width;
+ }
+}
+
+int AtlasTexture::get_height() const {
+ if (region.size.height == 0) {
+ if (atlas.is_valid()) {
+ return atlas->get_height();
+ }
+ return 1;
+ } else {
+ return region.size.height + margin.size.height;
+ }
+}
+
+RID AtlasTexture::get_rid() const {
+ if (atlas.is_valid()) {
+ return atlas->get_rid();
+ }
+
+ return RID();
+}
+
+bool AtlasTexture::has_alpha() const {
+ if (atlas.is_valid()) {
+ return atlas->has_alpha();
+ }
+
+ return false;
+}
+
+void AtlasTexture::set_atlas(const Ref<Texture2D> &p_atlas) {
+ ERR_FAIL_COND(p_atlas == this);
+ if (atlas == p_atlas) {
+ return;
+ }
+ // Support recursive AtlasTextures.
+ if (Ref<AtlasTexture>(atlas).is_valid()) {
+ atlas->disconnect_changed(callable_mp((Resource *)this, &AtlasTexture::emit_changed));
+ }
+ atlas = p_atlas;
+ if (Ref<AtlasTexture>(atlas).is_valid()) {
+ atlas->connect_changed(callable_mp((Resource *)this, &AtlasTexture::emit_changed));
+ }
+
+ emit_changed();
+}
+
+Ref<Texture2D> AtlasTexture::get_atlas() const {
+ return atlas;
+}
+
+void AtlasTexture::set_region(const Rect2 &p_region) {
+ if (region == p_region) {
+ return;
+ }
+ region = p_region;
+ emit_changed();
+}
+
+Rect2 AtlasTexture::get_region() const {
+ return region;
+}
+
+void AtlasTexture::set_margin(const Rect2 &p_margin) {
+ if (margin == p_margin) {
+ return;
+ }
+ margin = p_margin;
+ emit_changed();
+}
+
+Rect2 AtlasTexture::get_margin() const {
+ return margin;
+}
+
+void AtlasTexture::set_filter_clip(const bool p_enable) {
+ filter_clip = p_enable;
+ emit_changed();
+}
+
+bool AtlasTexture::has_filter_clip() const {
+ return filter_clip;
+}
+
+void AtlasTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_atlas", "atlas"), &AtlasTexture::set_atlas);
+ ClassDB::bind_method(D_METHOD("get_atlas"), &AtlasTexture::get_atlas);
+
+ ClassDB::bind_method(D_METHOD("set_region", "region"), &AtlasTexture::set_region);
+ ClassDB::bind_method(D_METHOD("get_region"), &AtlasTexture::get_region);
+
+ ClassDB::bind_method(D_METHOD("set_margin", "margin"), &AtlasTexture::set_margin);
+ ClassDB::bind_method(D_METHOD("get_margin"), &AtlasTexture::get_margin);
+
+ ClassDB::bind_method(D_METHOD("set_filter_clip", "enable"), &AtlasTexture::set_filter_clip);
+ ClassDB::bind_method(D_METHOD("has_filter_clip"), &AtlasTexture::has_filter_clip);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "atlas", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_atlas", "get_atlas");
+ ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region", PROPERTY_HINT_NONE, "suffix:px"), "set_region", "get_region");
+ ADD_PROPERTY(PropertyInfo(Variant::RECT2, "margin", PROPERTY_HINT_NONE, "suffix:px"), "set_margin", "get_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_clip"), "set_filter_clip", "has_filter_clip");
+}
+
+void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ if (!atlas.is_valid()) {
+ return;
+ }
+
+ Rect2 rc = region;
+
+ if (rc.size.width == 0) {
+ rc.size.width = atlas->get_width();
+ }
+
+ if (rc.size.height == 0) {
+ rc.size.height = atlas->get_height();
+ }
+
+ atlas->draw_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), rc, p_modulate, p_transpose, filter_clip);
+}
+
+void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ if (!atlas.is_valid()) {
+ return;
+ }
+
+ Rect2 rc = region;
+
+ if (rc.size.width == 0) {
+ rc.size.width = atlas->get_width();
+ }
+
+ if (rc.size.height == 0) {
+ rc.size.height = atlas->get_height();
+ }
+
+ Vector2 scale = p_rect.size / (region.size + margin.size);
+ Rect2 dr(p_rect.position + margin.position * scale, rc.size * scale);
+
+ atlas->draw_rect_region(p_canvas_item, dr, rc, p_modulate, p_transpose, filter_clip);
+}
+
+void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ //this might not necessarily work well if using a rect, needs to be fixed properly
+ if (!atlas.is_valid()) {
+ return;
+ }
+
+ Rect2 dr;
+ Rect2 src_c;
+ get_rect_region(p_rect, p_src_rect, dr, src_c);
+
+ atlas->draw_rect_region(p_canvas_item, dr, src_c, p_modulate, p_transpose, filter_clip);
+}
+
+bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
+ if (!atlas.is_valid()) {
+ return false;
+ }
+
+ Rect2 src = p_src_rect;
+ if (src.size == Size2()) {
+ src.size = region.size;
+ }
+ Vector2 scale = p_rect.size / src.size;
+
+ src.position += (region.position - margin.position);
+ Rect2 src_clipped = region.intersection(src);
+ if (src_clipped.size == Size2()) {
+ return false;
+ }
+
+ Vector2 ofs = (src_clipped.position - src.position);
+ if (scale.x < 0) {
+ ofs.x += (src_clipped.size.x - src.size.x);
+ }
+ if (scale.y < 0) {
+ ofs.y += (src_clipped.size.y - src.size.y);
+ }
+
+ r_rect = Rect2(p_rect.position + ofs * scale, src_clipped.size * scale);
+ r_src_rect = src_clipped;
+ return true;
+}
+
+bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const {
+ if (!atlas.is_valid()) {
+ return true;
+ }
+
+ int x = p_x + region.position.x - margin.position.x;
+ int y = p_y + region.position.y - margin.position.y;
+
+ // margin edge may outside of atlas
+ if (x < 0 || x >= atlas->get_width()) {
+ return false;
+ }
+ if (y < 0 || y >= atlas->get_height()) {
+ return false;
+ }
+
+ return atlas->is_pixel_opaque(x, y);
+}
+
+Ref<Image> AtlasTexture::get_image() const {
+ if (!atlas.is_valid() || !atlas->get_image().is_valid()) {
+ return Ref<Image>();
+ }
+
+ return atlas->get_image()->get_region(region);
+}
+
+AtlasTexture::AtlasTexture() {}
diff --git a/scene/resources/atlas_texture.h b/scene/resources/atlas_texture.h
new file mode 100644
index 0000000000..5aba098d09
--- /dev/null
+++ b/scene/resources/atlas_texture.h
@@ -0,0 +1,79 @@
+/**************************************************************************/
+/* atlas_texture.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 ATLAS_TEXTURE_H
+#define ATLAS_TEXTURE_H
+
+#include "scene/resources/texture.h"
+
+class AtlasTexture : public Texture2D {
+ GDCLASS(AtlasTexture, Texture2D);
+ RES_BASE_EXTENSION("atlastex");
+
+protected:
+ Ref<Texture2D> atlas;
+ Rect2 region;
+ Rect2 margin;
+ bool filter_clip = false;
+
+ static void _bind_methods();
+
+public:
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual RID get_rid() const override;
+
+ virtual bool has_alpha() const override;
+
+ void set_atlas(const Ref<Texture2D> &p_atlas);
+ Ref<Texture2D> get_atlas() const;
+
+ void set_region(const Rect2 &p_region);
+ Rect2 get_region() const;
+
+ void set_margin(const Rect2 &p_margin);
+ Rect2 get_margin() const;
+
+ void set_filter_clip(const bool p_enable);
+ bool has_filter_clip() const;
+
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
+ virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const override;
+
+ bool is_pixel_opaque(int p_x, int p_y) const override;
+
+ virtual Ref<Image> get_image() const override;
+
+ AtlasTexture();
+};
+
+#endif // ATLAS_TEXTURE_H
diff --git a/scene/resources/box_shape_3d.cpp b/scene/resources/box_shape_3d.cpp
index 6a1f7dc6ea..313aeb1bca 100644
--- a/scene/resources/box_shape_3d.cpp
+++ b/scene/resources/box_shape_3d.cpp
@@ -80,7 +80,7 @@ void BoxShape3D::set_size(const Vector3 &p_size) {
ERR_FAIL_COND_MSG(p_size.x < 0 || p_size.y < 0 || p_size.z < 0, "BoxShape3D size cannot be negative.");
size = p_size;
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
Vector3 BoxShape3D::get_size() const {
diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp
index 8f4f804397..7c46729af3 100644
--- a/scene/resources/camera_attributes.cpp
+++ b/scene/resources/camera_attributes.cpp
@@ -122,7 +122,7 @@ void CameraAttributes::_bind_methods() {
ADD_GROUP("Exposure", "exposure_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_sensitivity", PROPERTY_HINT_RANGE, "0.1,32000.0,0.1,suffix:ISO"), "set_exposure_sensitivity", "get_exposure_sensitivity");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_multiplier", PROPERTY_HINT_RANGE, "0.0,2048.0,0.001"), "set_exposure_multiplier", "get_exposure_multiplier");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_multiplier", PROPERTY_HINT_RANGE, "0.0,8.0,0.001,or_greater"), "set_exposure_multiplier", "get_exposure_multiplier");
ADD_GROUP("Auto Exposure", "auto_exposure_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_exposure_enabled"), "set_auto_exposure_enabled", "is_auto_exposure_enabled");
diff --git a/scene/resources/camera_texture.cpp b/scene/resources/camera_texture.cpp
new file mode 100644
index 0000000000..b575a099ed
--- /dev/null
+++ b/scene/resources/camera_texture.cpp
@@ -0,0 +1,131 @@
+/**************************************************************************/
+/* camera_texture.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 "camera_texture.h"
+
+#include "servers/camera/camera_feed.h"
+
+void CameraTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_camera_feed_id", "feed_id"), &CameraTexture::set_camera_feed_id);
+ ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &CameraTexture::get_camera_feed_id);
+
+ ClassDB::bind_method(D_METHOD("set_which_feed", "which_feed"), &CameraTexture::set_which_feed);
+ ClassDB::bind_method(D_METHOD("get_which_feed"), &CameraTexture::get_which_feed);
+
+ ClassDB::bind_method(D_METHOD("set_camera_active", "active"), &CameraTexture::set_camera_active);
+ ClassDB::bind_method(D_METHOD("get_camera_active"), &CameraTexture::get_camera_active);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "camera_feed_id"), "set_camera_feed_id", "get_camera_feed_id");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "which_feed"), "set_which_feed", "get_which_feed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "camera_is_active"), "set_camera_active", "get_camera_active");
+}
+
+int CameraTexture::get_width() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->get_base_width();
+ } else {
+ return 0;
+ }
+}
+
+int CameraTexture::get_height() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->get_base_height();
+ } else {
+ return 0;
+ }
+}
+
+bool CameraTexture::has_alpha() const {
+ return false;
+}
+
+RID CameraTexture::get_rid() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->get_texture(which_feed);
+ } else {
+ if (_texture.is_null()) {
+ _texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+ return _texture;
+ }
+}
+
+Ref<Image> CameraTexture::get_image() const {
+ // not (yet) supported
+ return Ref<Image>();
+}
+
+void CameraTexture::set_camera_feed_id(int p_new_id) {
+ camera_feed_id = p_new_id;
+ notify_property_list_changed();
+}
+
+int CameraTexture::get_camera_feed_id() const {
+ return camera_feed_id;
+}
+
+void CameraTexture::set_which_feed(CameraServer::FeedImage p_which) {
+ which_feed = p_which;
+ notify_property_list_changed();
+}
+
+CameraServer::FeedImage CameraTexture::get_which_feed() const {
+ return which_feed;
+}
+
+void CameraTexture::set_camera_active(bool p_active) {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ feed->set_active(p_active);
+ notify_property_list_changed();
+ }
+}
+
+bool CameraTexture::get_camera_active() const {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ return feed->is_active();
+ } else {
+ return false;
+ }
+}
+
+CameraTexture::CameraTexture() {}
+
+CameraTexture::~CameraTexture() {
+ if (_texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RenderingServer::get_singleton()->free(_texture);
+ }
+}
diff --git a/scene/resources/camera_texture.h b/scene/resources/camera_texture.h
new file mode 100644
index 0000000000..521121f9ea
--- /dev/null
+++ b/scene/resources/camera_texture.h
@@ -0,0 +1,68 @@
+/**************************************************************************/
+/* camera_texture.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 CAMERA_TEXTURE_H
+#define CAMERA_TEXTURE_H
+
+#include "scene/resources/texture.h"
+
+class CameraTexture : public Texture2D {
+ GDCLASS(CameraTexture, Texture2D);
+
+private:
+ mutable RID _texture;
+ int camera_feed_id = 0;
+ CameraServer::FeedImage which_feed = CameraServer::FEED_RGBA_IMAGE;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual RID get_rid() const override;
+ virtual bool has_alpha() const override;
+
+ virtual Ref<Image> get_image() const override;
+
+ void set_camera_feed_id(int p_new_id);
+ int get_camera_feed_id() const;
+
+ void set_which_feed(CameraServer::FeedImage p_which);
+ CameraServer::FeedImage get_which_feed() const;
+
+ void set_camera_active(bool p_active);
+ bool get_camera_active() const;
+
+ CameraTexture();
+ ~CameraTexture();
+};
+
+#endif // CAMERA_TEXTURE_H
diff --git a/scene/resources/capsule_shape_3d.cpp b/scene/resources/capsule_shape_3d.cpp
index 3edff0cc68..9e16801060 100644
--- a/scene/resources/capsule_shape_3d.cpp
+++ b/scene/resources/capsule_shape_3d.cpp
@@ -86,7 +86,7 @@ void CapsuleShape3D::set_radius(float p_radius) {
height = radius * 2.0;
}
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
float CapsuleShape3D::get_radius() const {
@@ -100,7 +100,7 @@ void CapsuleShape3D::set_height(float p_height) {
radius = height * 0.5;
}
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
float CapsuleShape3D::get_height() const {
diff --git a/scene/resources/compressed_texture.cpp b/scene/resources/compressed_texture.cpp
new file mode 100644
index 0000000000..f4395d5c7d
--- /dev/null
+++ b/scene/resources/compressed_texture.cpp
@@ -0,0 +1,907 @@
+/**************************************************************************/
+/* compressed_texture.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 "compressed_texture.h"
+
+#include "scene/resources/bit_map.h"
+
+Error CompressedTexture2D::_load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
+ alpha_cache.unref();
+
+ ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER);
+
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
+
+ uint8_t header[4];
+ f->get_buffer(header, 4);
+ if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != '2') {
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is corrupt (Bad header).");
+ }
+
+ uint32_t version = f->get_32();
+
+ if (version > FORMAT_VERSION) {
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
+ }
+ r_width = f->get_32();
+ r_height = f->get_32();
+ uint32_t df = f->get_32(); //data format
+
+ //skip reserved
+ mipmap_limit = int(f->get_32());
+ //reserved
+ f->get_32();
+ f->get_32();
+ f->get_32();
+
+#ifdef TOOLS_ENABLED
+
+ r_request_3d = request_3d_callback && df & FORMAT_BIT_DETECT_3D;
+ r_request_roughness = request_roughness_callback && df & FORMAT_BIT_DETECT_ROUGNESS;
+ r_request_normal = request_normal_callback && df & FORMAT_BIT_DETECT_NORMAL;
+
+#else
+
+ r_request_3d = false;
+ r_request_roughness = false;
+ r_request_normal = false;
+
+#endif
+ if (!(df & FORMAT_BIT_STREAM)) {
+ p_size_limit = 0;
+ }
+
+ image = load_image_from_file(f, p_size_limit);
+
+ if (image.is_null() || image->is_empty()) {
+ return ERR_CANT_OPEN;
+ }
+
+ return OK;
+}
+
+void CompressedTexture2D::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+void CompressedTexture2D::_requested_3d(void *p_ud) {
+ CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
+ Ref<CompressedTexture2D> ctex(ct);
+ ERR_FAIL_NULL(request_3d_callback);
+ request_3d_callback(ctex);
+}
+
+void CompressedTexture2D::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) {
+ CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
+ Ref<CompressedTexture2D> ctex(ct);
+ ERR_FAIL_NULL(request_roughness_callback);
+ request_roughness_callback(ctex, p_normal_path, p_roughness_channel);
+}
+
+void CompressedTexture2D::_requested_normal(void *p_ud) {
+ CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
+ Ref<CompressedTexture2D> ctex(ct);
+ ERR_FAIL_NULL(request_normal_callback);
+ request_normal_callback(ctex);
+}
+
+CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_3d_callback = nullptr;
+CompressedTexture2D::TextureFormatRoughnessRequestCallback CompressedTexture2D::request_roughness_callback = nullptr;
+CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_normal_callback = nullptr;
+
+Image::Format CompressedTexture2D::get_format() const {
+ return format;
+}
+
+Error CompressedTexture2D::load(const String &p_path) {
+ int lw, lh;
+ Ref<Image> image;
+ image.instantiate();
+
+ bool request_3d;
+ bool request_normal;
+ bool request_roughness;
+ int mipmap_limit;
+
+ Error err = _load_data(p_path, lw, lh, image, request_3d, request_normal, request_roughness, mipmap_limit);
+ if (err) {
+ return err;
+ }
+
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_2d_create(image);
+ }
+ if (lw || lh) {
+ RS::get_singleton()->texture_set_size_override(texture, lw, lh);
+ }
+
+ w = lw;
+ h = lh;
+ path_to_file = p_path;
+ format = image->get_format();
+
+ if (get_path().is_empty()) {
+ //temporarily set path if no path set for resource, helps find errors
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+#ifdef TOOLS_ENABLED
+
+ if (request_3d) {
+ //print_line("request detect 3D at " + p_path);
+ RS::get_singleton()->texture_set_detect_3d_callback(texture, _requested_3d, this);
+ } else {
+ //print_line("not requesting detect 3D at " + p_path);
+ RS::get_singleton()->texture_set_detect_3d_callback(texture, nullptr, nullptr);
+ }
+
+ if (request_roughness) {
+ //print_line("request detect srgb at " + p_path);
+ RS::get_singleton()->texture_set_detect_roughness_callback(texture, _requested_roughness, this);
+ } else {
+ //print_line("not requesting detect srgb at " + p_path);
+ RS::get_singleton()->texture_set_detect_roughness_callback(texture, nullptr, nullptr);
+ }
+
+ if (request_normal) {
+ //print_line("request detect srgb at " + p_path);
+ RS::get_singleton()->texture_set_detect_normal_callback(texture, _requested_normal, this);
+ } else {
+ //print_line("not requesting detect normal at " + p_path);
+ RS::get_singleton()->texture_set_detect_normal_callback(texture, nullptr, nullptr);
+ }
+
+#endif
+ notify_property_list_changed();
+ emit_changed();
+ return OK;
+}
+
+String CompressedTexture2D::get_load_path() const {
+ return path_to_file;
+}
+
+int CompressedTexture2D::get_width() const {
+ return w;
+}
+
+int CompressedTexture2D::get_height() const {
+ return h;
+}
+
+RID CompressedTexture2D::get_rid() const {
+ if (!texture.is_valid()) {
+ texture = RS::get_singleton()->texture_2d_placeholder_create();
+ }
+ return texture;
+}
+
+void CompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ if ((w | h) == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
+}
+
+void CompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ if ((w | h) == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
+}
+
+void CompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ if ((w | h) == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
+}
+
+bool CompressedTexture2D::has_alpha() const {
+ return false;
+}
+
+Ref<Image> CompressedTexture2D::get_image() const {
+ if (texture.is_valid()) {
+ return RS::get_singleton()->texture_2d_get(texture);
+ } else {
+ return Ref<Image>();
+ }
+}
+
+bool CompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
+ if (!alpha_cache.is_valid()) {
+ Ref<Image> img = get_image();
+ if (img.is_valid()) {
+ if (img->is_compressed()) { //must decompress, if compressed
+ Ref<Image> decom = img->duplicate();
+ decom->decompress();
+ img = decom;
+ }
+
+ alpha_cache.instantiate();
+ alpha_cache->create_from_image_alpha(img);
+ }
+ }
+
+ if (alpha_cache.is_valid()) {
+ int aw = int(alpha_cache->get_size().width);
+ int ah = int(alpha_cache->get_size().height);
+ if (aw == 0 || ah == 0) {
+ return true;
+ }
+
+ int x = p_x * aw / w;
+ int y = p_y * ah / h;
+
+ x = CLAMP(x, 0, aw);
+ y = CLAMP(y, 0, ah);
+
+ return alpha_cache->get_bit(x, y);
+ }
+
+ return true;
+}
+
+void CompressedTexture2D::reload_from_file() {
+ String path = get_path();
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ path = ResourceLoader::path_remap(path); //remap for translation
+ path = ResourceLoader::import_remap(path); //remap for import
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ load(path);
+}
+
+void CompressedTexture2D::_validate_property(PropertyInfo &p_property) const {
+}
+
+Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_size_limit) {
+ uint32_t data_format = f->get_32();
+ uint32_t w = f->get_16();
+ uint32_t h = f->get_16();
+ uint32_t mipmaps = f->get_32();
+ Image::Format format = Image::Format(f->get_32());
+
+ if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP) {
+ //look for a PNG or WebP file inside
+
+ int sw = w;
+ int sh = h;
+
+ //mipmaps need to be read independently, they will be later combined
+ Vector<Ref<Image>> mipmap_images;
+ uint64_t total_size = 0;
+
+ bool first = true;
+
+ for (uint32_t i = 0; i < mipmaps + 1; i++) {
+ uint32_t size = f->get_32();
+
+ if (p_size_limit > 0 && i < (mipmaps - 1) && (sw > p_size_limit || sh > p_size_limit)) {
+ //can't load this due to size limit
+ sw = MAX(sw >> 1, 1);
+ sh = MAX(sh >> 1, 1);
+ f->seek(f->get_position() + size);
+ continue;
+ }
+
+ Vector<uint8_t> pv;
+ pv.resize(size);
+ {
+ uint8_t *wr = pv.ptrw();
+ f->get_buffer(wr, size);
+ }
+
+ Ref<Image> img;
+ if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) {
+ img = Image::png_unpacker(pv);
+ } else if (data_format == DATA_FORMAT_WEBP && Image::webp_unpacker) {
+ img = Image::webp_unpacker(pv);
+ }
+
+ if (img.is_null() || img->is_empty()) {
+ ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());
+ }
+
+ if (first) {
+ //format will actually be the format of the first image,
+ //as it may have changed on compression
+ format = img->get_format();
+ first = false;
+ } else if (img->get_format() != format) {
+ img->convert(format); //all needs to be the same format
+ }
+
+ total_size += img->get_data().size();
+
+ mipmap_images.push_back(img);
+
+ sw = MAX(sw >> 1, 1);
+ sh = MAX(sh >> 1, 1);
+ }
+
+ //print_line("mipmap read total: " + itos(mipmap_images.size()));
+
+ Ref<Image> image;
+ image.instantiate();
+
+ if (mipmap_images.size() == 1) {
+ //only one image (which will most likely be the case anyway for this format)
+ image = mipmap_images[0];
+ return image;
+
+ } else {
+ //rarer use case, but needs to be supported
+ Vector<uint8_t> img_data;
+ img_data.resize(total_size);
+
+ {
+ uint8_t *wr = img_data.ptrw();
+
+ int ofs = 0;
+ for (int i = 0; i < mipmap_images.size(); i++) {
+ Vector<uint8_t> id = mipmap_images[i]->get_data();
+ int len = id.size();
+ const uint8_t *r = id.ptr();
+ memcpy(&wr[ofs], r, len);
+ ofs += len;
+ }
+ }
+
+ image->set_data(w, h, true, mipmap_images[0]->get_format(), img_data);
+ return image;
+ }
+
+ } else if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) {
+ int sw = w;
+ int sh = h;
+ uint32_t size = f->get_32();
+ if (p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) {
+ //can't load this due to size limit
+ sw = MAX(sw >> 1, 1);
+ sh = MAX(sh >> 1, 1);
+ f->seek(f->get_position() + size);
+ return Ref<Image>();
+ }
+ Vector<uint8_t> pv;
+ pv.resize(size);
+ {
+ uint8_t *wr = pv.ptrw();
+ f->get_buffer(wr, size);
+ }
+ Ref<Image> img;
+ img = Image::basis_universal_unpacker(pv);
+ if (img.is_null() || img->is_empty()) {
+ ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());
+ }
+ format = img->get_format();
+ sw = MAX(sw >> 1, 1);
+ sh = MAX(sh >> 1, 1);
+ return img;
+ } else if (data_format == DATA_FORMAT_IMAGE) {
+ int size = Image::get_image_data_size(w, h, format, mipmaps ? true : false);
+
+ for (uint32_t i = 0; i < mipmaps + 1; i++) {
+ int tw, th;
+ int ofs = Image::get_image_mipmap_offset_and_dimensions(w, h, format, i, tw, th);
+
+ if (p_size_limit > 0 && i < mipmaps && (p_size_limit > tw || p_size_limit > th)) {
+ if (ofs) {
+ f->seek(f->get_position() + ofs);
+ }
+ continue; //oops, size limit enforced, go to next
+ }
+
+ Vector<uint8_t> data;
+ data.resize(size - ofs);
+
+ {
+ uint8_t *wr = data.ptrw();
+ f->get_buffer(wr, data.size());
+ }
+
+ Ref<Image> image = Image::create_from_data(tw, th, mipmaps - i ? true : false, format, data);
+
+ return image;
+ }
+ }
+
+ return Ref<Image>();
+}
+
+void CompressedTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture2D::load);
+ ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture2D::get_load_path);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
+}
+
+CompressedTexture2D::CompressedTexture2D() {}
+
+CompressedTexture2D::~CompressedTexture2D() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(texture);
+ }
+}
+
+Ref<Resource> ResourceFormatLoaderCompressedTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ Ref<CompressedTexture2D> st;
+ st.instantiate();
+ Error err = st->load(p_path);
+ if (r_error) {
+ *r_error = err;
+ }
+ if (err != OK) {
+ return Ref<Resource>();
+ }
+
+ return st;
+}
+
+void ResourceFormatLoaderCompressedTexture2D::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("ctex");
+}
+
+bool ResourceFormatLoaderCompressedTexture2D::handles_type(const String &p_type) const {
+ return p_type == "CompressedTexture2D";
+}
+
+String ResourceFormatLoaderCompressedTexture2D::get_resource_type(const String &p_path) const {
+ if (p_path.get_extension().to_lower() == "ctex") {
+ return "CompressedTexture2D";
+ }
+ return "";
+}
+
+void CompressedTexture3D::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+Image::Format CompressedTexture3D::get_format() const {
+ return format;
+}
+
+Error CompressedTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
+
+ uint8_t header[4];
+ f->get_buffer(header, 4);
+ ERR_FAIL_COND_V(header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L', ERR_FILE_UNRECOGNIZED);
+
+ //stored as compressed textures (used for lossless and lossy compression)
+ uint32_t version = f->get_32();
+
+ if (version > FORMAT_VERSION) {
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
+ }
+
+ r_depth = f->get_32(); //depth
+ f->get_32(); //ignored (mode)
+ f->get_32(); // ignored (data format)
+
+ f->get_32(); //ignored
+ int mipmap_count = f->get_32();
+ f->get_32(); //ignored
+ f->get_32(); //ignored
+
+ r_mipmaps = mipmap_count != 0;
+
+ r_data.clear();
+
+ for (int i = 0; i < (r_depth + mipmap_count); i++) {
+ Ref<Image> image = CompressedTexture2D::load_image_from_file(f, 0);
+ ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
+ if (i == 0) {
+ r_format = image->get_format();
+ r_width = image->get_width();
+ r_height = image->get_height();
+ }
+ r_data.push_back(image);
+ }
+
+ return OK;
+}
+
+Error CompressedTexture3D::load(const String &p_path) {
+ Vector<Ref<Image>> data;
+
+ int tw, th, td;
+ Image::Format tfmt;
+ bool tmm;
+
+ Error err = _load_data(p_path, data, tfmt, tw, th, td, tmm);
+ if (err) {
+ return err;
+ }
+
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data);
+ }
+
+ w = tw;
+ h = th;
+ d = td;
+ mipmaps = tmm;
+ format = tfmt;
+
+ path_to_file = p_path;
+
+ if (get_path().is_empty()) {
+ //temporarily set path if no path set for resource, helps find errors
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ notify_property_list_changed();
+ emit_changed();
+ return OK;
+}
+
+String CompressedTexture3D::get_load_path() const {
+ return path_to_file;
+}
+
+int CompressedTexture3D::get_width() const {
+ return w;
+}
+
+int CompressedTexture3D::get_height() const {
+ return h;
+}
+
+int CompressedTexture3D::get_depth() const {
+ return d;
+}
+
+bool CompressedTexture3D::has_mipmaps() const {
+ return mipmaps;
+}
+
+RID CompressedTexture3D::get_rid() const {
+ if (!texture.is_valid()) {
+ texture = RS::get_singleton()->texture_3d_placeholder_create();
+ }
+ return texture;
+}
+
+Vector<Ref<Image>> CompressedTexture3D::get_data() const {
+ if (texture.is_valid()) {
+ return RS::get_singleton()->texture_3d_get(texture);
+ } else {
+ return Vector<Ref<Image>>();
+ }
+}
+
+void CompressedTexture3D::reload_from_file() {
+ String path = get_path();
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ path = ResourceLoader::path_remap(path); //remap for translation
+ path = ResourceLoader::import_remap(path); //remap for import
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ load(path);
+}
+
+void CompressedTexture3D::_validate_property(PropertyInfo &p_property) const {
+}
+
+void CompressedTexture3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture3D::load);
+ ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture3D::get_load_path);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
+}
+
+CompressedTexture3D::CompressedTexture3D() {}
+
+CompressedTexture3D::~CompressedTexture3D() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(texture);
+ }
+}
+
+Ref<Resource> ResourceFormatLoaderCompressedTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ Ref<CompressedTexture3D> st;
+ st.instantiate();
+ Error err = st->load(p_path);
+ if (r_error) {
+ *r_error = err;
+ }
+ if (err != OK) {
+ return Ref<Resource>();
+ }
+
+ return st;
+}
+
+void ResourceFormatLoaderCompressedTexture3D::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("ctex3d");
+}
+
+bool ResourceFormatLoaderCompressedTexture3D::handles_type(const String &p_type) const {
+ return p_type == "CompressedTexture3D";
+}
+
+String ResourceFormatLoaderCompressedTexture3D::get_resource_type(const String &p_path) const {
+ if (p_path.get_extension().to_lower() == "ctex3d") {
+ return "CompressedTexture3D";
+ }
+ return "";
+}
+
+void CompressedTextureLayered::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+Image::Format CompressedTextureLayered::get_format() const {
+ return format;
+}
+
+Error CompressedTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) {
+ ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER);
+
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
+
+ uint8_t header[4];
+ f->get_buffer(header, 4);
+ if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L') {
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture layered file is corrupt (Bad header).");
+ }
+
+ uint32_t version = f->get_32();
+
+ if (version > FORMAT_VERSION) {
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
+ }
+
+ uint32_t layer_count = f->get_32(); //layer count
+ uint32_t type = f->get_32(); //layer count
+ ERR_FAIL_COND_V((int)type != layered_type, ERR_INVALID_DATA);
+
+ uint32_t df = f->get_32(); //data format
+ mipmap_limit = int(f->get_32());
+ //reserved
+ f->get_32();
+ f->get_32();
+ f->get_32();
+
+ if (!(df & FORMAT_BIT_STREAM)) {
+ p_size_limit = 0;
+ }
+
+ images.resize(layer_count);
+
+ for (uint32_t i = 0; i < layer_count; i++) {
+ Ref<Image> image = CompressedTexture2D::load_image_from_file(f, p_size_limit);
+ ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
+ images.write[i] = image;
+ }
+
+ return OK;
+}
+
+Error CompressedTextureLayered::load(const String &p_path) {
+ Vector<Ref<Image>> images;
+
+ int mipmap_limit;
+
+ Error err = _load_data(p_path, images, mipmap_limit);
+ if (err) {
+ return err;
+ }
+
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));
+ }
+
+ w = images[0]->get_width();
+ h = images[0]->get_height();
+ mipmaps = images[0]->has_mipmaps();
+ format = images[0]->get_format();
+ layers = images.size();
+
+ path_to_file = p_path;
+
+ if (get_path().is_empty()) {
+ //temporarily set path if no path set for resource, helps find errors
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ notify_property_list_changed();
+ emit_changed();
+ return OK;
+}
+
+String CompressedTextureLayered::get_load_path() const {
+ return path_to_file;
+}
+
+int CompressedTextureLayered::get_width() const {
+ return w;
+}
+
+int CompressedTextureLayered::get_height() const {
+ return h;
+}
+
+int CompressedTextureLayered::get_layers() const {
+ return layers;
+}
+
+bool CompressedTextureLayered::has_mipmaps() const {
+ return mipmaps;
+}
+
+TextureLayered::LayeredType CompressedTextureLayered::get_layered_type() const {
+ return layered_type;
+}
+
+RID CompressedTextureLayered::get_rid() const {
+ if (!texture.is_valid()) {
+ texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
+ }
+ return texture;
+}
+
+Ref<Image> CompressedTextureLayered::get_layer_data(int p_layer) const {
+ if (texture.is_valid()) {
+ return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
+ } else {
+ return Ref<Image>();
+ }
+}
+
+void CompressedTextureLayered::reload_from_file() {
+ String path = get_path();
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ path = ResourceLoader::path_remap(path); //remap for translation
+ path = ResourceLoader::import_remap(path); //remap for import
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ load(path);
+}
+
+void CompressedTextureLayered::_validate_property(PropertyInfo &p_property) const {
+}
+
+void CompressedTextureLayered::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTextureLayered::load);
+ ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTextureLayered::get_load_path);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
+}
+
+CompressedTextureLayered::CompressedTextureLayered(LayeredType p_type) {
+ layered_type = p_type;
+}
+
+CompressedTextureLayered::~CompressedTextureLayered() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(texture);
+ }
+}
+
+/////////////////////////////////////////////////
+
+Ref<Resource> ResourceFormatLoaderCompressedTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ Ref<CompressedTextureLayered> ct;
+ if (p_path.get_extension().to_lower() == "ctexarray") {
+ Ref<CompressedTexture2DArray> c;
+ c.instantiate();
+ ct = c;
+ } else if (p_path.get_extension().to_lower() == "ccube") {
+ Ref<CompressedCubemap> c;
+ c.instantiate();
+ ct = c;
+ } else if (p_path.get_extension().to_lower() == "ccubearray") {
+ Ref<CompressedCubemapArray> c;
+ c.instantiate();
+ ct = c;
+ } else {
+ if (r_error) {
+ *r_error = ERR_FILE_UNRECOGNIZED;
+ }
+ return Ref<Resource>();
+ }
+ Error err = ct->load(p_path);
+ if (r_error) {
+ *r_error = err;
+ }
+ if (err != OK) {
+ return Ref<Resource>();
+ }
+
+ return ct;
+}
+
+void ResourceFormatLoaderCompressedTextureLayered::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("ctexarray");
+ p_extensions->push_back("ccube");
+ p_extensions->push_back("ccubearray");
+}
+
+bool ResourceFormatLoaderCompressedTextureLayered::handles_type(const String &p_type) const {
+ return p_type == "CompressedTexture2DArray" || p_type == "CompressedCubemap" || p_type == "CompressedCubemapArray";
+}
+
+String ResourceFormatLoaderCompressedTextureLayered::get_resource_type(const String &p_path) const {
+ if (p_path.get_extension().to_lower() == "ctexarray") {
+ return "CompressedTexture2DArray";
+ }
+ if (p_path.get_extension().to_lower() == "ccube") {
+ return "CompressedCubemap";
+ }
+ if (p_path.get_extension().to_lower() == "ccubearray") {
+ return "CompressedCubemapArray";
+ }
+ return "";
+}
diff --git a/scene/resources/compressed_texture.h b/scene/resources/compressed_texture.h
new file mode 100644
index 0000000000..5297d79cfe
--- /dev/null
+++ b/scene/resources/compressed_texture.h
@@ -0,0 +1,273 @@
+/**************************************************************************/
+/* compressed_texture.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 COMPRESSED_TEXTURE_H
+#define COMPRESSED_TEXTURE_H
+
+#include "scene/resources/texture.h"
+
+class BitMap;
+
+class CompressedTexture2D : public Texture2D {
+ GDCLASS(CompressedTexture2D, Texture2D);
+
+public:
+ enum DataFormat {
+ DATA_FORMAT_IMAGE,
+ DATA_FORMAT_PNG,
+ DATA_FORMAT_WEBP,
+ DATA_FORMAT_BASIS_UNIVERSAL,
+ };
+
+ enum {
+ FORMAT_VERSION = 1
+ };
+
+ enum FormatBits {
+ FORMAT_BIT_STREAM = 1 << 22,
+ FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
+ FORMAT_BIT_DETECT_3D = 1 << 24,
+ //FORMAT_BIT_DETECT_SRGB = 1 << 25,
+ FORMAT_BIT_DETECT_NORMAL = 1 << 26,
+ FORMAT_BIT_DETECT_ROUGNESS = 1 << 27,
+ };
+
+private:
+ String path_to_file;
+ mutable RID texture;
+ Image::Format format = Image::FORMAT_L8;
+ int w = 0;
+ int h = 0;
+ mutable Ref<BitMap> alpha_cache;
+
+ Error _load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit = 0);
+ virtual void reload_from_file() override;
+
+ static void _requested_3d(void *p_ud);
+ static void _requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel);
+ static void _requested_normal(void *p_ud);
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const;
+
+public:
+ static Ref<Image> load_image_from_file(Ref<FileAccess> p_file, int p_size_limit);
+
+ typedef void (*TextureFormatRequestCallback)(const Ref<CompressedTexture2D> &);
+ typedef void (*TextureFormatRoughnessRequestCallback)(const Ref<CompressedTexture2D> &, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel);
+
+ static TextureFormatRequestCallback request_3d_callback;
+ static TextureFormatRoughnessRequestCallback request_roughness_callback;
+ static TextureFormatRequestCallback request_normal_callback;
+
+ Image::Format get_format() const;
+ Error load(const String &p_path);
+ String get_load_path() const;
+
+ int get_width() const override;
+ int get_height() const override;
+ virtual RID get_rid() const override;
+
+ virtual void set_path(const String &p_path, bool p_take_over) override;
+
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
+
+ virtual bool has_alpha() const override;
+ bool is_pixel_opaque(int p_x, int p_y) const override;
+
+ virtual Ref<Image> get_image() const override;
+
+ CompressedTexture2D();
+ ~CompressedTexture2D();
+};
+
+class ResourceFormatLoaderCompressedTexture2D : public ResourceFormatLoader {
+public:
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
+class CompressedTextureLayered : public TextureLayered {
+ GDCLASS(CompressedTextureLayered, TextureLayered);
+
+public:
+ enum DataFormat {
+ DATA_FORMAT_IMAGE,
+ DATA_FORMAT_PNG,
+ DATA_FORMAT_WEBP,
+ DATA_FORMAT_BASIS_UNIVERSAL,
+ };
+
+ enum {
+ FORMAT_VERSION = 1
+ };
+
+ enum FormatBits {
+ FORMAT_BIT_STREAM = 1 << 22,
+ FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
+ };
+
+private:
+ Error _load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit = 0);
+ String path_to_file;
+ mutable RID texture;
+ Image::Format format = Image::FORMAT_L8;
+ int w = 0;
+ int h = 0;
+ int layers = 0;
+ bool mipmaps = false;
+ LayeredType layered_type = LayeredType::LAYERED_TYPE_2D_ARRAY;
+
+ virtual void reload_from_file() override;
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const;
+
+public:
+ Image::Format get_format() const override;
+ Error load(const String &p_path);
+ String get_load_path() const;
+ virtual LayeredType get_layered_type() const override;
+
+ int get_width() const override;
+ int get_height() const override;
+ int get_layers() const override;
+ virtual bool has_mipmaps() const override;
+ virtual RID get_rid() const override;
+
+ virtual void set_path(const String &p_path, bool p_take_over) override;
+
+ virtual Ref<Image> get_layer_data(int p_layer) const override;
+
+ CompressedTextureLayered(LayeredType p_layered_type);
+ ~CompressedTextureLayered();
+};
+
+class ResourceFormatLoaderCompressedTextureLayered : public ResourceFormatLoader {
+public:
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
+class CompressedTexture2DArray : public CompressedTextureLayered {
+ GDCLASS(CompressedTexture2DArray, CompressedTextureLayered)
+public:
+ CompressedTexture2DArray() :
+ CompressedTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
+};
+
+class CompressedCubemap : public CompressedTextureLayered {
+ GDCLASS(CompressedCubemap, CompressedTextureLayered);
+
+public:
+ CompressedCubemap() :
+ CompressedTextureLayered(LAYERED_TYPE_CUBEMAP) {}
+};
+
+class CompressedCubemapArray : public CompressedTextureLayered {
+ GDCLASS(CompressedCubemapArray, CompressedTextureLayered);
+
+public:
+ CompressedCubemapArray() :
+ CompressedTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
+};
+
+class CompressedTexture3D : public Texture3D {
+ GDCLASS(CompressedTexture3D, Texture3D);
+
+public:
+ enum DataFormat {
+ DATA_FORMAT_IMAGE,
+ DATA_FORMAT_PNG,
+ DATA_FORMAT_WEBP,
+ DATA_FORMAT_BASIS_UNIVERSAL,
+ };
+
+ enum {
+ FORMAT_VERSION = 1
+ };
+
+ enum FormatBits {
+ FORMAT_BIT_STREAM = 1 << 22,
+ FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
+ };
+
+private:
+ Error _load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps);
+ String path_to_file;
+ mutable RID texture;
+ Image::Format format = Image::FORMAT_L8;
+ int w = 0;
+ int h = 0;
+ int d = 0;
+ bool mipmaps = false;
+
+ virtual void reload_from_file() override;
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const;
+
+public:
+ Image::Format get_format() const override;
+ Error load(const String &p_path);
+ String get_load_path() const;
+
+ int get_width() const override;
+ int get_height() const override;
+ int get_depth() const override;
+ virtual bool has_mipmaps() const override;
+ virtual RID get_rid() const override;
+
+ virtual void set_path(const String &p_path, bool p_take_over) override;
+
+ virtual Vector<Ref<Image>> get_data() const override;
+
+ CompressedTexture3D();
+ ~CompressedTexture3D();
+};
+
+class ResourceFormatLoaderCompressedTexture3D : public ResourceFormatLoader {
+public:
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
+#endif // COMPRESSED_TEXTURE_H
diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp
index cb5e0fc600..82b125905f 100644
--- a/scene/resources/concave_polygon_shape_3d.cpp
+++ b/scene/resources/concave_polygon_shape_3d.cpp
@@ -81,7 +81,7 @@ void ConcavePolygonShape3D::_update_shape() {
void ConcavePolygonShape3D::set_faces(const Vector<Vector3> &p_faces) {
faces = p_faces;
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
Vector<Vector3> ConcavePolygonShape3D::get_faces() const {
@@ -93,7 +93,7 @@ void ConcavePolygonShape3D::set_backface_collision_enabled(bool p_enabled) {
if (!faces.is_empty()) {
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
}
diff --git a/scene/resources/convex_polygon_shape_3d.cpp b/scene/resources/convex_polygon_shape_3d.cpp
index cc0ef8f583..3bfeeca461 100644
--- a/scene/resources/convex_polygon_shape_3d.cpp
+++ b/scene/resources/convex_polygon_shape_3d.cpp
@@ -71,7 +71,7 @@ void ConvexPolygonShape3D::_update_shape() {
void ConvexPolygonShape3D::set_points(const Vector<Vector3> &p_points) {
points = p_points;
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
Vector<Vector3> ConvexPolygonShape3D::get_points() const {
diff --git a/scene/resources/curve_texture.cpp b/scene/resources/curve_texture.cpp
new file mode 100644
index 0000000000..3578b46308
--- /dev/null
+++ b/scene/resources/curve_texture.cpp
@@ -0,0 +1,376 @@
+/**************************************************************************/
+/* curve_texture.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 "curve_texture.h"
+
+#include "core/core_string_names.h"
+
+void CurveTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width);
+
+ ClassDB::bind_method(D_METHOD("set_curve", "curve"), &CurveTexture::set_curve);
+ ClassDB::bind_method(D_METHOD("get_curve"), &CurveTexture::get_curve);
+
+ ClassDB::bind_method(D_METHOD("set_texture_mode", "texture_mode"), &CurveTexture::set_texture_mode);
+ ClassDB::bind_method(D_METHOD("get_texture_mode"), &CurveTexture::get_texture_mode);
+
+ ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096,suffix:px"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "RGB,Red"), "set_texture_mode", "get_texture_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
+
+ BIND_ENUM_CONSTANT(TEXTURE_MODE_RGB);
+ BIND_ENUM_CONSTANT(TEXTURE_MODE_RED);
+}
+
+void CurveTexture::set_width(int p_width) {
+ ERR_FAIL_COND(p_width < 32 || p_width > 4096);
+
+ if (_width == p_width) {
+ return;
+ }
+
+ _width = p_width;
+ _update();
+}
+
+int CurveTexture::get_width() const {
+ return _width;
+}
+
+void CurveTexture::ensure_default_setup(float p_min, float p_max) {
+ if (_curve.is_null()) {
+ Ref<Curve> curve = Ref<Curve>(memnew(Curve));
+ curve->add_point(Vector2(0, 1));
+ curve->add_point(Vector2(1, 1));
+ curve->set_min_value(p_min);
+ curve->set_max_value(p_max);
+ set_curve(curve);
+ // Min and max is 0..1 by default
+ }
+}
+
+void CurveTexture::set_curve(Ref<Curve> p_curve) {
+ if (_curve != p_curve) {
+ if (_curve.is_valid()) {
+ _curve->disconnect_changed(callable_mp(this, &CurveTexture::_update));
+ }
+ _curve = p_curve;
+ if (_curve.is_valid()) {
+ _curve->connect_changed(callable_mp(this, &CurveTexture::_update));
+ }
+ _update();
+ }
+}
+
+void CurveTexture::_update() {
+ Vector<uint8_t> data;
+ data.resize(_width * sizeof(float) * (texture_mode == TEXTURE_MODE_RGB ? 3 : 1));
+
+ // The array is locked in that scope
+ {
+ uint8_t *wd8 = data.ptrw();
+ float *wd = (float *)wd8;
+
+ if (_curve.is_valid()) {
+ Curve &curve = **_curve;
+ for (int i = 0; i < _width; ++i) {
+ float t = i / static_cast<float>(_width);
+ if (texture_mode == TEXTURE_MODE_RGB) {
+ wd[i * 3 + 0] = curve.sample_baked(t);
+ wd[i * 3 + 1] = wd[i * 3 + 0];
+ wd[i * 3 + 2] = wd[i * 3 + 0];
+ } else {
+ wd[i] = curve.sample_baked(t);
+ }
+ }
+
+ } else {
+ for (int i = 0; i < _width; ++i) {
+ if (texture_mode == TEXTURE_MODE_RGB) {
+ wd[i * 3 + 0] = 0;
+ wd[i * 3 + 1] = 0;
+ wd[i * 3 + 2] = 0;
+ } else {
+ wd[i] = 0;
+ }
+ }
+ }
+ }
+
+ Ref<Image> image = memnew(Image(_width, 1, false, texture_mode == TEXTURE_MODE_RGB ? Image::FORMAT_RGBF : Image::FORMAT_RF, data));
+
+ if (_texture.is_valid()) {
+ if (_current_texture_mode != texture_mode || _current_width != _width) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(_texture, new_texture);
+ } else {
+ RS::get_singleton()->texture_2d_update(_texture, image);
+ }
+ } else {
+ _texture = RS::get_singleton()->texture_2d_create(image);
+ }
+ _current_texture_mode = texture_mode;
+ _current_width = _width;
+
+ emit_changed();
+}
+
+Ref<Curve> CurveTexture::get_curve() const {
+ return _curve;
+}
+
+void CurveTexture::set_texture_mode(TextureMode p_mode) {
+ ERR_FAIL_COND(p_mode < TEXTURE_MODE_RGB || p_mode > TEXTURE_MODE_RED);
+ if (texture_mode == p_mode) {
+ return;
+ }
+ texture_mode = p_mode;
+ _update();
+}
+CurveTexture::TextureMode CurveTexture::get_texture_mode() const {
+ return texture_mode;
+}
+
+RID CurveTexture::get_rid() const {
+ if (!_texture.is_valid()) {
+ _texture = RS::get_singleton()->texture_2d_placeholder_create();
+ }
+ return _texture;
+}
+
+CurveTexture::CurveTexture() {}
+
+CurveTexture::~CurveTexture() {
+ if (_texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(_texture);
+ }
+}
+
+//////////////////
+
+void CurveXYZTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveXYZTexture::set_width);
+
+ ClassDB::bind_method(D_METHOD("set_curve_x", "curve"), &CurveXYZTexture::set_curve_x);
+ ClassDB::bind_method(D_METHOD("get_curve_x"), &CurveXYZTexture::get_curve_x);
+
+ ClassDB::bind_method(D_METHOD("set_curve_y", "curve"), &CurveXYZTexture::set_curve_y);
+ ClassDB::bind_method(D_METHOD("get_curve_y"), &CurveXYZTexture::get_curve_y);
+
+ ClassDB::bind_method(D_METHOD("set_curve_z", "curve"), &CurveXYZTexture::set_curve_z);
+ ClassDB::bind_method(D_METHOD("get_curve_z"), &CurveXYZTexture::get_curve_z);
+
+ ClassDB::bind_method(D_METHOD("_update"), &CurveXYZTexture::_update);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096,suffix:px"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_x", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_x", "get_curve_x");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_y", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_y", "get_curve_y");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_z", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_z", "get_curve_z");
+}
+
+void CurveXYZTexture::set_width(int p_width) {
+ ERR_FAIL_COND(p_width < 32 || p_width > 4096);
+
+ if (_width == p_width) {
+ return;
+ }
+
+ _width = p_width;
+ _update();
+}
+
+int CurveXYZTexture::get_width() const {
+ return _width;
+}
+
+void CurveXYZTexture::ensure_default_setup(float p_min, float p_max) {
+ if (_curve_x.is_null()) {
+ Ref<Curve> curve = Ref<Curve>(memnew(Curve));
+ curve->add_point(Vector2(0, 1));
+ curve->add_point(Vector2(1, 1));
+ curve->set_min_value(p_min);
+ curve->set_max_value(p_max);
+ set_curve_x(curve);
+ }
+
+ if (_curve_y.is_null()) {
+ Ref<Curve> curve = Ref<Curve>(memnew(Curve));
+ curve->add_point(Vector2(0, 1));
+ curve->add_point(Vector2(1, 1));
+ curve->set_min_value(p_min);
+ curve->set_max_value(p_max);
+ set_curve_y(curve);
+ }
+
+ if (_curve_z.is_null()) {
+ Ref<Curve> curve = Ref<Curve>(memnew(Curve));
+ curve->add_point(Vector2(0, 1));
+ curve->add_point(Vector2(1, 1));
+ curve->set_min_value(p_min);
+ curve->set_max_value(p_max);
+ set_curve_z(curve);
+ }
+}
+
+void CurveXYZTexture::set_curve_x(Ref<Curve> p_curve) {
+ if (_curve_x != p_curve) {
+ if (_curve_x.is_valid()) {
+ _curve_x->disconnect_changed(callable_mp(this, &CurveXYZTexture::_update));
+ }
+ _curve_x = p_curve;
+ if (_curve_x.is_valid()) {
+ _curve_x->connect_changed(callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED);
+ }
+ _update();
+ }
+}
+
+void CurveXYZTexture::set_curve_y(Ref<Curve> p_curve) {
+ if (_curve_y != p_curve) {
+ if (_curve_y.is_valid()) {
+ _curve_y->disconnect_changed(callable_mp(this, &CurveXYZTexture::_update));
+ }
+ _curve_y = p_curve;
+ if (_curve_y.is_valid()) {
+ _curve_y->connect_changed(callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED);
+ }
+ _update();
+ }
+}
+
+void CurveXYZTexture::set_curve_z(Ref<Curve> p_curve) {
+ if (_curve_z != p_curve) {
+ if (_curve_z.is_valid()) {
+ _curve_z->disconnect_changed(callable_mp(this, &CurveXYZTexture::_update));
+ }
+ _curve_z = p_curve;
+ if (_curve_z.is_valid()) {
+ _curve_z->connect_changed(callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED);
+ }
+ _update();
+ }
+}
+
+void CurveXYZTexture::_update() {
+ Vector<uint8_t> data;
+ data.resize(_width * sizeof(float) * 3);
+
+ // The array is locked in that scope
+ {
+ uint8_t *wd8 = data.ptrw();
+ float *wd = (float *)wd8;
+
+ if (_curve_x.is_valid()) {
+ Curve &curve_x = **_curve_x;
+ for (int i = 0; i < _width; ++i) {
+ float t = i / static_cast<float>(_width);
+ wd[i * 3 + 0] = curve_x.sample_baked(t);
+ }
+
+ } else {
+ for (int i = 0; i < _width; ++i) {
+ wd[i * 3 + 0] = 0;
+ }
+ }
+
+ if (_curve_y.is_valid()) {
+ Curve &curve_y = **_curve_y;
+ for (int i = 0; i < _width; ++i) {
+ float t = i / static_cast<float>(_width);
+ wd[i * 3 + 1] = curve_y.sample_baked(t);
+ }
+
+ } else {
+ for (int i = 0; i < _width; ++i) {
+ wd[i * 3 + 1] = 0;
+ }
+ }
+
+ if (_curve_z.is_valid()) {
+ Curve &curve_z = **_curve_z;
+ for (int i = 0; i < _width; ++i) {
+ float t = i / static_cast<float>(_width);
+ wd[i * 3 + 2] = curve_z.sample_baked(t);
+ }
+
+ } else {
+ for (int i = 0; i < _width; ++i) {
+ wd[i * 3 + 2] = 0;
+ }
+ }
+ }
+
+ Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RGBF, data));
+
+ if (_texture.is_valid()) {
+ if (_current_width != _width) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(_texture, new_texture);
+ } else {
+ RS::get_singleton()->texture_2d_update(_texture, image);
+ }
+ } else {
+ _texture = RS::get_singleton()->texture_2d_create(image);
+ }
+ _current_width = _width;
+
+ emit_changed();
+}
+
+Ref<Curve> CurveXYZTexture::get_curve_x() const {
+ return _curve_x;
+}
+
+Ref<Curve> CurveXYZTexture::get_curve_y() const {
+ return _curve_y;
+}
+
+Ref<Curve> CurveXYZTexture::get_curve_z() const {
+ return _curve_z;
+}
+
+RID CurveXYZTexture::get_rid() const {
+ if (!_texture.is_valid()) {
+ _texture = RS::get_singleton()->texture_2d_placeholder_create();
+ }
+ return _texture;
+}
+
+CurveXYZTexture::CurveXYZTexture() {}
+
+CurveXYZTexture::~CurveXYZTexture() {
+ if (_texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(_texture);
+ }
+}
diff --git a/scene/resources/curve_texture.h b/scene/resources/curve_texture.h
new file mode 100644
index 0000000000..fd590aefa9
--- /dev/null
+++ b/scene/resources/curve_texture.h
@@ -0,0 +1,122 @@
+/**************************************************************************/
+/* curve_texture.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 CURVE_TEXTURE_H
+#define CURVE_TEXTURE_H
+
+#include "scene/resources/texture.h"
+
+class CurveTexture : public Texture2D {
+ GDCLASS(CurveTexture, Texture2D);
+ RES_BASE_EXTENSION("curvetex")
+public:
+ enum TextureMode {
+ TEXTURE_MODE_RGB,
+ TEXTURE_MODE_RED,
+ };
+
+private:
+ mutable RID _texture;
+ Ref<Curve> _curve;
+ int _width = 256;
+ int _current_width = 0;
+ TextureMode texture_mode = TEXTURE_MODE_RGB;
+ TextureMode _current_texture_mode = TEXTURE_MODE_RGB;
+
+ void _update();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_width(int p_width);
+ int get_width() const override;
+
+ void set_texture_mode(TextureMode p_mode);
+ TextureMode get_texture_mode() const;
+
+ void ensure_default_setup(float p_min = 0, float p_max = 1);
+
+ void set_curve(Ref<Curve> p_curve);
+ Ref<Curve> get_curve() const;
+
+ virtual RID get_rid() const override;
+
+ virtual int get_height() const override { return 1; }
+ virtual bool has_alpha() const override { return false; }
+
+ CurveTexture();
+ ~CurveTexture();
+};
+
+VARIANT_ENUM_CAST(CurveTexture::TextureMode)
+
+class CurveXYZTexture : public Texture2D {
+ GDCLASS(CurveXYZTexture, Texture2D);
+ RES_BASE_EXTENSION("curvetex")
+
+private:
+ mutable RID _texture;
+ Ref<Curve> _curve_x;
+ Ref<Curve> _curve_y;
+ Ref<Curve> _curve_z;
+ int _width = 256;
+ int _current_width = 0;
+
+ void _update();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_width(int p_width);
+ int get_width() const override;
+
+ void ensure_default_setup(float p_min = 0, float p_max = 1);
+
+ void set_curve_x(Ref<Curve> p_curve);
+ Ref<Curve> get_curve_x() const;
+
+ void set_curve_y(Ref<Curve> p_curve);
+ Ref<Curve> get_curve_y() const;
+
+ void set_curve_z(Ref<Curve> p_curve);
+ Ref<Curve> get_curve_z() const;
+
+ virtual RID get_rid() const override;
+
+ virtual int get_height() const override { return 1; }
+ virtual bool has_alpha() const override { return false; }
+
+ CurveXYZTexture();
+ ~CurveXYZTexture();
+};
+
+#endif // CURVE_TEXTURE_H
diff --git a/scene/resources/cylinder_shape_3d.cpp b/scene/resources/cylinder_shape_3d.cpp
index aee2963b2e..a91282fd33 100644
--- a/scene/resources/cylinder_shape_3d.cpp
+++ b/scene/resources/cylinder_shape_3d.cpp
@@ -76,7 +76,7 @@ void CylinderShape3D::set_radius(float p_radius) {
ERR_FAIL_COND_MSG(p_radius < 0, "CylinderShape3D radius cannot be negative.");
radius = p_radius;
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
float CylinderShape3D::get_radius() const {
@@ -87,7 +87,7 @@ void CylinderShape3D::set_height(float p_height) {
ERR_FAIL_COND_MSG(p_height < 0, "CylinderShape3D height cannot be negative.");
height = p_height;
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
float CylinderShape3D::get_height() const {
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 07f38473cb..24ffaca68d 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -34,6 +34,10 @@
#include "default_font.gen.h"
#include "default_theme_icons.gen.h"
#include "scene/resources/font.h"
+#include "scene/resources/gradient_texture.h"
+#include "scene/resources/image_texture.h"
+#include "scene/resources/style_box_flat.h"
+#include "scene/resources/style_box_line.h"
#include "scene/resources/theme.h"
#include "scene/theme/theme_db.h"
#include "servers/text_server.h"
@@ -595,6 +599,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Window
theme->set_stylebox("embedded_border", "Window", sb_expand(make_flat_stylebox(style_popup_color, 10, 28, 10, 8), 8, 32, 8, 6));
+ theme->set_stylebox("embedded_unfocused_border", "Window", sb_expand(make_flat_stylebox(style_popup_hover_color, 10, 28, 10, 8), 8, 32, 8, 6));
theme->set_font("title_font", "Window", Ref<Font>());
theme->set_font_size("title_font_size", "Window", -1);
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 757be51017..e48f744c72 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -31,9 +31,8 @@
#include "environment.h"
#include "core/config/project_settings.h"
-#include "core/core_string_names.h"
+#include "scene/resources/gradient_texture.h"
#include "servers/rendering_server.h"
-#include "texture.h"
RID Environment::get_rid() const {
return environment;
@@ -1004,9 +1003,7 @@ void Environment::set_adjustment_color_correction(Ref<Texture> p_color_correctio
adjustment_color_correction = p_color_correction;
Ref<GradientTexture1D> grad_tex = p_color_correction;
if (grad_tex.is_valid()) {
- if (!grad_tex->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment))) {
- grad_tex->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Environment::_update_adjustment));
- }
+ grad_tex->connect_changed(callable_mp(this, &Environment::_update_adjustment));
}
Ref<Texture2D> adjustment_texture_2d = adjustment_color_correction;
if (adjustment_texture_2d.is_valid()) {
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index d07d9913a6..09a835db1b 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -30,12 +30,12 @@
#include "font.h"
-#include "core/core_string_names.h"
#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
#include "core/string/translation.h"
#include "core/templates/hash_map.h"
#include "core/templates/hashfuncs.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/text_line.h"
#include "scene/resources/text_paragraph.h"
#include "scene/resources/theme.h"
@@ -159,14 +159,14 @@ void Font::set_fallbacks(const TypedArray<Font> &p_fallbacks) {
for (int i = 0; i < fallbacks.size(); i++) {
Ref<Font> f = fallbacks[i];
if (f.is_valid()) {
- f->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids));
+ f->disconnect_changed(callable_mp(this, &Font::_invalidate_rids));
}
}
fallbacks = p_fallbacks;
for (int i = 0; i < fallbacks.size(); i++) {
Ref<Font> f = fallbacks[i];
if (f.is_valid()) {
- f->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ f->connect_changed(callable_mp(this, &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
}
_invalidate_rids();
@@ -2674,12 +2674,12 @@ void FontVariation::_update_rids() const {
void FontVariation::reset_state() {
if (base_font.is_valid()) {
- base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ base_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
base_font.unref();
}
if (theme_font.is_valid()) {
- theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ theme_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
theme_font.unref();
}
@@ -2696,11 +2696,11 @@ void FontVariation::reset_state() {
void FontVariation::set_base_font(const Ref<Font> &p_font) {
if (base_font != p_font) {
if (base_font.is_valid()) {
- base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ base_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
}
base_font = p_font;
if (base_font.is_valid()) {
- base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ base_font->connect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
_invalidate_rids();
notify_property_list_changed();
@@ -2713,7 +2713,7 @@ Ref<Font> FontVariation::get_base_font() const {
Ref<Font> FontVariation::_get_base_font_or_default() const {
if (theme_font.is_valid()) {
- theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids));
+ theme_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids));
theme_font.unref();
}
@@ -2734,7 +2734,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const {
}
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@@ -2754,7 +2754,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const {
}
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@@ -2765,7 +2765,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const {
if (f != this) {
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@@ -2949,7 +2949,7 @@ void SystemFont::_update_rids() const {
void SystemFont::_update_base_font() {
if (base_font.is_valid()) {
- base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ base_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
base_font.unref();
}
@@ -3030,7 +3030,7 @@ void SystemFont::_update_base_font() {
}
if (base_font.is_valid()) {
- base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ base_font->connect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
_invalidate_rids();
@@ -3039,12 +3039,12 @@ void SystemFont::_update_base_font() {
void SystemFont::reset_state() {
if (base_font.is_valid()) {
- base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ base_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
base_font.unref();
}
if (theme_font.is_valid()) {
- theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
+ theme_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids));
theme_font.unref();
}
@@ -3070,7 +3070,7 @@ void SystemFont::reset_state() {
Ref<Font> SystemFont::_get_base_font_or_default() const {
if (theme_font.is_valid()) {
- theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids));
+ theme_font->disconnect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids));
theme_font.unref();
}
@@ -3091,7 +3091,7 @@ Ref<Font> SystemFont::_get_base_font_or_default() const {
}
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@@ -3111,7 +3111,7 @@ Ref<Font> SystemFont::_get_base_font_or_default() const {
}
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
@@ -3122,7 +3122,7 @@ Ref<Font> SystemFont::_get_base_font_or_default() const {
if (f != this) {
if (f.is_valid()) {
theme_font = f;
- theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
+ theme_font->connect_changed(callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED);
}
return f;
}
diff --git a/scene/resources/gradient_texture.cpp b/scene/resources/gradient_texture.cpp
new file mode 100644
index 0000000000..20868faaa2
--- /dev/null
+++ b/scene/resources/gradient_texture.cpp
@@ -0,0 +1,438 @@
+/**************************************************************************/
+/* gradient_texture.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 "gradient_texture.h"
+
+#include "core/core_string_names.h"
+#include "core/math/geometry_2d.h"
+
+GradientTexture1D::GradientTexture1D() {
+ _queue_update();
+}
+
+GradientTexture1D::~GradientTexture1D() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(texture);
+ }
+}
+
+void GradientTexture1D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture1D::set_gradient);
+ ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture1D::get_gradient);
+
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture1D::set_width);
+ // The `get_width()` method is already exposed by the parent class Texture2D.
+
+ ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture1D::set_use_hdr);
+ ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture1D::is_using_hdr);
+
+ ClassDB::bind_method(D_METHOD("_update"), &GradientTexture1D::_update);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,16384,suffix:px"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
+}
+
+void GradientTexture1D::set_gradient(Ref<Gradient> p_gradient) {
+ if (p_gradient == gradient) {
+ return;
+ }
+ if (gradient.is_valid()) {
+ gradient->disconnect_changed(callable_mp(this, &GradientTexture1D::_update));
+ }
+ gradient = p_gradient;
+ if (gradient.is_valid()) {
+ gradient->connect_changed(callable_mp(this, &GradientTexture1D::_update));
+ }
+ _update();
+ emit_changed();
+}
+
+Ref<Gradient> GradientTexture1D::get_gradient() const {
+ return gradient;
+}
+
+void GradientTexture1D::_queue_update() {
+ if (update_pending) {
+ return;
+ }
+
+ update_pending = true;
+ call_deferred(SNAME("_update"));
+}
+
+void GradientTexture1D::_update() {
+ update_pending = false;
+
+ if (gradient.is_null()) {
+ return;
+ }
+
+ if (use_hdr) {
+ // High dynamic range.
+ Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBAF));
+ Gradient &g = **gradient;
+ // `create()` isn't available for non-uint8_t data, so fill in the data manually.
+ for (int i = 0; i < width; i++) {
+ float ofs = float(i) / (width - 1);
+ image->set_pixel(i, 0, g.get_color_at_offset(ofs));
+ }
+
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_2d_create(image);
+ }
+ } else {
+ // Low dynamic range. "Overbright" colors will be clamped.
+ Vector<uint8_t> data;
+ data.resize(width * 4);
+ {
+ uint8_t *wd8 = data.ptrw();
+ Gradient &g = **gradient;
+
+ for (int i = 0; i < width; i++) {
+ float ofs = float(i) / (width - 1);
+ Color color = g.get_color_at_offset(ofs);
+
+ wd8[i * 4 + 0] = uint8_t(CLAMP(color.r * 255.0, 0, 255));
+ wd8[i * 4 + 1] = uint8_t(CLAMP(color.g * 255.0, 0, 255));
+ wd8[i * 4 + 2] = uint8_t(CLAMP(color.b * 255.0, 0, 255));
+ wd8[i * 4 + 3] = uint8_t(CLAMP(color.a * 255.0, 0, 255));
+ }
+ }
+
+ Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data));
+
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_2d_create(image);
+ }
+ }
+
+ emit_changed();
+}
+
+void GradientTexture1D::set_width(int p_width) {
+ ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be within 1 to 16384 range.");
+ width = p_width;
+ _queue_update();
+}
+
+int GradientTexture1D::get_width() const {
+ return width;
+}
+
+void GradientTexture1D::set_use_hdr(bool p_enabled) {
+ if (p_enabled == use_hdr) {
+ return;
+ }
+
+ use_hdr = p_enabled;
+ _queue_update();
+}
+
+bool GradientTexture1D::is_using_hdr() const {
+ return use_hdr;
+}
+
+Ref<Image> GradientTexture1D::get_image() const {
+ if (!texture.is_valid()) {
+ return Ref<Image>();
+ }
+ return RenderingServer::get_singleton()->texture_2d_get(texture);
+}
+
+//////////////////
+
+GradientTexture2D::GradientTexture2D() {
+ _queue_update();
+}
+
+GradientTexture2D::~GradientTexture2D() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(texture);
+ }
+}
+
+void GradientTexture2D::set_gradient(Ref<Gradient> p_gradient) {
+ if (gradient == p_gradient) {
+ return;
+ }
+ if (gradient.is_valid()) {
+ gradient->disconnect_changed(callable_mp(this, &GradientTexture2D::_queue_update));
+ }
+ gradient = p_gradient;
+ if (gradient.is_valid()) {
+ gradient->connect_changed(callable_mp(this, &GradientTexture2D::_queue_update));
+ }
+ _update();
+ emit_changed();
+}
+
+Ref<Gradient> GradientTexture2D::get_gradient() const {
+ return gradient;
+}
+
+void GradientTexture2D::_queue_update() {
+ if (update_pending) {
+ return;
+ }
+ update_pending = true;
+ call_deferred(SNAME("_update"));
+}
+
+void GradientTexture2D::_update() {
+ update_pending = false;
+
+ if (gradient.is_null()) {
+ return;
+ }
+ Ref<Image> image;
+ image.instantiate();
+
+ if (gradient->get_point_count() <= 1) { // No need to interpolate.
+ image->initialize_data(width, height, false, (use_hdr) ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8);
+ image->fill((gradient->get_point_count() == 1) ? gradient->get_color(0) : Color(0, 0, 0, 1));
+ } else {
+ if (use_hdr) {
+ image->initialize_data(width, height, false, Image::FORMAT_RGBAF);
+ Gradient &g = **gradient;
+ // `create()` isn't available for non-uint8_t data, so fill in the data manually.
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ float ofs = _get_gradient_offset_at(x, y);
+ image->set_pixel(x, y, g.get_color_at_offset(ofs));
+ }
+ }
+ } else {
+ Vector<uint8_t> data;
+ data.resize(width * height * 4);
+ {
+ uint8_t *wd8 = data.ptrw();
+ Gradient &g = **gradient;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ float ofs = _get_gradient_offset_at(x, y);
+ const Color &c = g.get_color_at_offset(ofs);
+
+ wd8[(x + (y * width)) * 4 + 0] = uint8_t(CLAMP(c.r * 255.0, 0, 255));
+ wd8[(x + (y * width)) * 4 + 1] = uint8_t(CLAMP(c.g * 255.0, 0, 255));
+ wd8[(x + (y * width)) * 4 + 2] = uint8_t(CLAMP(c.b * 255.0, 0, 255));
+ wd8[(x + (y * width)) * 4 + 3] = uint8_t(CLAMP(c.a * 255.0, 0, 255));
+ }
+ }
+ }
+ image->set_data(width, height, false, Image::FORMAT_RGBA8, data);
+ }
+ }
+
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_2d_create(image);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_2d_create(image);
+ }
+ emit_changed();
+}
+
+float GradientTexture2D::_get_gradient_offset_at(int x, int y) const {
+ if (fill_to == fill_from) {
+ return 0;
+ }
+ float ofs = 0;
+ Vector2 pos;
+ if (width > 1) {
+ pos.x = static_cast<float>(x) / (width - 1);
+ }
+ if (height > 1) {
+ pos.y = static_cast<float>(y) / (height - 1);
+ }
+ if (fill == Fill::FILL_LINEAR) {
+ Vector2 segment[2];
+ segment[0] = fill_from;
+ segment[1] = fill_to;
+ Vector2 closest = Geometry2D::get_closest_point_to_segment_uncapped(pos, &segment[0]);
+ ofs = (closest - fill_from).length() / (fill_to - fill_from).length();
+ if ((closest - fill_from).dot(fill_to - fill_from) < 0) {
+ ofs *= -1;
+ }
+ } else if (fill == Fill::FILL_RADIAL) {
+ ofs = (pos - fill_from).length() / (fill_to - fill_from).length();
+ } else if (fill == Fill::FILL_SQUARE) {
+ ofs = MAX(Math::abs(pos.x - fill_from.x), Math::abs(pos.y - fill_from.y)) / MAX(Math::abs(fill_to.x - fill_from.x), Math::abs(fill_to.y - fill_from.y));
+ }
+ if (repeat == Repeat::REPEAT_NONE) {
+ ofs = CLAMP(ofs, 0.0, 1.0);
+ } else if (repeat == Repeat::REPEAT) {
+ ofs = Math::fmod(ofs, 1.0f);
+ if (ofs < 0) {
+ ofs = 1 + ofs;
+ }
+ } else if (repeat == Repeat::REPEAT_MIRROR) {
+ ofs = Math::abs(ofs);
+ ofs = Math::fmod(ofs, 2.0f);
+ if (ofs > 1.0) {
+ ofs = 2.0 - ofs;
+ }
+ }
+ return ofs;
+}
+
+void GradientTexture2D::set_width(int p_width) {
+ ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be within 1 to 16384 range.");
+ width = p_width;
+ _queue_update();
+}
+
+int GradientTexture2D::get_width() const {
+ return width;
+}
+
+void GradientTexture2D::set_height(int p_height) {
+ ERR_FAIL_COND_MSG(p_height <= 0 || p_height > 16384, "Texture dimensions have to be within 1 to 16384 range.");
+ height = p_height;
+ _queue_update();
+}
+int GradientTexture2D::get_height() const {
+ return height;
+}
+
+void GradientTexture2D::set_use_hdr(bool p_enabled) {
+ if (p_enabled == use_hdr) {
+ return;
+ }
+
+ use_hdr = p_enabled;
+ _queue_update();
+}
+
+bool GradientTexture2D::is_using_hdr() const {
+ return use_hdr;
+}
+
+void GradientTexture2D::set_fill_from(Vector2 p_fill_from) {
+ fill_from = p_fill_from;
+ _queue_update();
+}
+
+Vector2 GradientTexture2D::get_fill_from() const {
+ return fill_from;
+}
+
+void GradientTexture2D::set_fill_to(Vector2 p_fill_to) {
+ fill_to = p_fill_to;
+ _queue_update();
+}
+
+Vector2 GradientTexture2D::get_fill_to() const {
+ return fill_to;
+}
+
+void GradientTexture2D::set_fill(Fill p_fill) {
+ fill = p_fill;
+ _queue_update();
+}
+
+GradientTexture2D::Fill GradientTexture2D::get_fill() const {
+ return fill;
+}
+
+void GradientTexture2D::set_repeat(Repeat p_repeat) {
+ repeat = p_repeat;
+ _queue_update();
+}
+
+GradientTexture2D::Repeat GradientTexture2D::get_repeat() const {
+ return repeat;
+}
+
+RID GradientTexture2D::get_rid() const {
+ if (!texture.is_valid()) {
+ texture = RS::get_singleton()->texture_2d_placeholder_create();
+ }
+ return texture;
+}
+
+Ref<Image> GradientTexture2D::get_image() const {
+ if (!texture.is_valid()) {
+ return Ref<Image>();
+ }
+ return RenderingServer::get_singleton()->texture_2d_get(texture);
+}
+
+void GradientTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture2D::set_gradient);
+ ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture2D::get_gradient);
+
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture2D::set_width);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &GradientTexture2D::set_height);
+
+ ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture2D::set_use_hdr);
+ ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture2D::is_using_hdr);
+
+ ClassDB::bind_method(D_METHOD("set_fill", "fill"), &GradientTexture2D::set_fill);
+ ClassDB::bind_method(D_METHOD("get_fill"), &GradientTexture2D::get_fill);
+ ClassDB::bind_method(D_METHOD("set_fill_from", "fill_from"), &GradientTexture2D::set_fill_from);
+ ClassDB::bind_method(D_METHOD("get_fill_from"), &GradientTexture2D::get_fill_from);
+ ClassDB::bind_method(D_METHOD("set_fill_to", "fill_to"), &GradientTexture2D::set_fill_to);
+ ClassDB::bind_method(D_METHOD("get_fill_to"), &GradientTexture2D::get_fill_to);
+
+ ClassDB::bind_method(D_METHOD("set_repeat", "repeat"), &GradientTexture2D::set_repeat);
+ ClassDB::bind_method(D_METHOD("get_repeat"), &GradientTexture2D::get_repeat);
+
+ ClassDB::bind_method(D_METHOD("_update"), &GradientTexture2D::_update);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,or_greater,suffix:px"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,or_greater,suffix:px"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
+
+ ADD_GROUP("Fill", "fill_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fill", PROPERTY_HINT_ENUM, "Linear,Radial,Square"), "set_fill", "get_fill");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_from"), "set_fill_from", "get_fill_from");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_to"), "set_fill_to", "get_fill_to");
+
+ ADD_GROUP("Repeat", "repeat_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "repeat", PROPERTY_HINT_ENUM, "No Repeat,Repeat,Mirror Repeat"), "set_repeat", "get_repeat");
+
+ BIND_ENUM_CONSTANT(FILL_LINEAR);
+ BIND_ENUM_CONSTANT(FILL_RADIAL);
+ BIND_ENUM_CONSTANT(FILL_SQUARE);
+
+ BIND_ENUM_CONSTANT(REPEAT_NONE);
+ BIND_ENUM_CONSTANT(REPEAT);
+ BIND_ENUM_CONSTANT(REPEAT_MIRROR);
+}
diff --git a/scene/resources/gradient_texture.h b/scene/resources/gradient_texture.h
new file mode 100644
index 0000000000..b8768ce0a8
--- /dev/null
+++ b/scene/resources/gradient_texture.h
@@ -0,0 +1,144 @@
+/**************************************************************************/
+/* gradient_texture.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 GRADIENT_TEXTURE_H
+#define GRADIENT_TEXTURE_H
+
+#include "scene/resources/texture.h"
+
+class GradientTexture1D : public Texture2D {
+ GDCLASS(GradientTexture1D, Texture2D);
+
+private:
+ Ref<Gradient> gradient;
+ bool update_pending = false;
+ RID texture;
+ int width = 256;
+ bool use_hdr = false;
+
+ void _queue_update();
+ void _update();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_gradient(Ref<Gradient> p_gradient);
+ Ref<Gradient> get_gradient() const;
+
+ void set_width(int p_width);
+ int get_width() const override;
+
+ void set_use_hdr(bool p_enabled);
+ bool is_using_hdr() const;
+
+ virtual RID get_rid() const override { return texture; }
+ virtual int get_height() const override { return 1; }
+ virtual bool has_alpha() const override { return true; }
+
+ virtual Ref<Image> get_image() const override;
+
+ GradientTexture1D();
+ virtual ~GradientTexture1D();
+};
+
+class GradientTexture2D : public Texture2D {
+ GDCLASS(GradientTexture2D, Texture2D);
+
+public:
+ enum Fill {
+ FILL_LINEAR,
+ FILL_RADIAL,
+ FILL_SQUARE,
+ };
+ enum Repeat {
+ REPEAT_NONE,
+ REPEAT,
+ REPEAT_MIRROR,
+ };
+
+private:
+ Ref<Gradient> gradient;
+ mutable RID texture;
+
+ int width = 64;
+ int height = 64;
+
+ bool use_hdr = false;
+
+ Vector2 fill_from;
+ Vector2 fill_to = Vector2(1, 0);
+
+ Fill fill = FILL_LINEAR;
+ Repeat repeat = REPEAT_NONE;
+
+ float _get_gradient_offset_at(int x, int y) const;
+
+ bool update_pending = false;
+ void _queue_update();
+ void _update();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_gradient(Ref<Gradient> p_gradient);
+ Ref<Gradient> get_gradient() const;
+
+ void set_width(int p_width);
+ virtual int get_width() const override;
+ void set_height(int p_height);
+ virtual int get_height() const override;
+
+ void set_use_hdr(bool p_enabled);
+ bool is_using_hdr() const;
+
+ void set_fill(Fill p_fill);
+ Fill get_fill() const;
+ void set_fill_from(Vector2 p_fill_from);
+ Vector2 get_fill_from() const;
+ void set_fill_to(Vector2 p_fill_to);
+ Vector2 get_fill_to() const;
+
+ void set_repeat(Repeat p_repeat);
+ Repeat get_repeat() const;
+
+ virtual RID get_rid() const override;
+ virtual bool has_alpha() const override { return true; }
+ virtual Ref<Image> get_image() const override;
+
+ GradientTexture2D();
+ virtual ~GradientTexture2D();
+};
+
+VARIANT_ENUM_CAST(GradientTexture2D::Fill);
+VARIANT_ENUM_CAST(GradientTexture2D::Repeat);
+
+#endif // GRADIENT_TEXTURE_H
diff --git a/scene/resources/height_map_shape_3d.cpp b/scene/resources/height_map_shape_3d.cpp
index 553daa93e6..718d701811 100644
--- a/scene/resources/height_map_shape_3d.cpp
+++ b/scene/resources/height_map_shape_3d.cpp
@@ -112,7 +112,7 @@ void HeightMapShape3D::set_map_width(int p_new) {
}
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
}
@@ -136,7 +136,7 @@ void HeightMapShape3D::set_map_depth(int p_new) {
}
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
}
@@ -172,7 +172,7 @@ void HeightMapShape3D::set_map_data(Vector<real_t> p_new) {
}
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
Vector<real_t> HeightMapShape3D::get_map_data() const {
diff --git a/scene/resources/image_texture.cpp b/scene/resources/image_texture.cpp
new file mode 100644
index 0000000000..4ec81dce65
--- /dev/null
+++ b/scene/resources/image_texture.cpp
@@ -0,0 +1,514 @@
+/**************************************************************************/
+/* image_texture.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 "image_texture.h"
+
+#include "core/io/image_loader.h"
+#include "scene/resources/bit_map.h"
+#include "scene/resources/placeholder_textures.h"
+
+void ImageTexture::reload_from_file() {
+ String path = ResourceLoader::path_remap(get_path());
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ Ref<Image> img;
+ img.instantiate();
+
+ if (ImageLoader::load_image(path, img) == OK) {
+ set_image(img);
+ } else {
+ Resource::reload_from_file();
+ notify_property_list_changed();
+ emit_changed();
+ }
+}
+
+bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "image") {
+ set_image(p_value);
+ return true;
+ }
+ return false;
+}
+
+bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "image") {
+ r_ret = get_image();
+ return true;
+ }
+ return false;
+}
+
+void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("image"), PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT));
+}
+
+Ref<ImageTexture> ImageTexture::create_from_image(const Ref<Image> &p_image) {
+ ERR_FAIL_COND_V_MSG(p_image.is_null(), Ref<ImageTexture>(), "Invalid image: null");
+ ERR_FAIL_COND_V_MSG(p_image->is_empty(), Ref<ImageTexture>(), "Invalid image: image is empty");
+
+ Ref<ImageTexture> image_texture;
+ image_texture.instantiate();
+ image_texture->set_image(p_image);
+ return image_texture;
+}
+
+void ImageTexture::set_image(const Ref<Image> &p_image) {
+ ERR_FAIL_COND_MSG(p_image.is_null() || p_image->is_empty(), "Invalid image");
+ w = p_image->get_width();
+ h = p_image->get_height();
+ format = p_image->get_format();
+ mipmaps = p_image->has_mipmaps();
+
+ if (texture.is_null()) {
+ texture = RenderingServer::get_singleton()->texture_2d_create(p_image);
+ } else {
+ RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_image);
+ RenderingServer::get_singleton()->texture_replace(texture, new_texture);
+ }
+ notify_property_list_changed();
+ emit_changed();
+
+ image_stored = true;
+}
+
+Image::Format ImageTexture::get_format() const {
+ return format;
+}
+
+void ImageTexture::update(const Ref<Image> &p_image) {
+ ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image");
+ ERR_FAIL_COND_MSG(texture.is_null(), "Texture is not initialized.");
+ ERR_FAIL_COND_MSG(p_image->get_width() != w || p_image->get_height() != h,
+ "The new image dimensions must match the texture size.");
+ ERR_FAIL_COND_MSG(p_image->get_format() != format,
+ "The new image format must match the texture's image format.");
+ ERR_FAIL_COND_MSG(mipmaps != p_image->has_mipmaps(),
+ "The new image mipmaps configuration must match the texture's image mipmaps configuration");
+
+ RS::get_singleton()->texture_2d_update(texture, p_image);
+
+ notify_property_list_changed();
+ emit_changed();
+
+ alpha_cache.unref();
+ image_stored = true;
+}
+
+Ref<Image> ImageTexture::get_image() const {
+ if (image_stored) {
+ return RenderingServer::get_singleton()->texture_2d_get(texture);
+ } else {
+ return Ref<Image>();
+ }
+}
+
+int ImageTexture::get_width() const {
+ return w;
+}
+
+int ImageTexture::get_height() const {
+ return h;
+}
+
+RID ImageTexture::get_rid() const {
+ if (texture.is_null()) {
+ //we are in trouble, create something temporary
+ texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+ return texture;
+}
+
+bool ImageTexture::has_alpha() const {
+ return (format == Image::FORMAT_LA8 || format == Image::FORMAT_RGBA8);
+}
+
+void ImageTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ if ((w | h) == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
+}
+
+void ImageTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ if ((w | h) == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
+}
+
+void ImageTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ if ((w | h) == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
+}
+
+bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const {
+ if (!alpha_cache.is_valid()) {
+ Ref<Image> img = get_image();
+ if (img.is_valid()) {
+ if (img->is_compressed()) { //must decompress, if compressed
+ Ref<Image> decom = img->duplicate();
+ decom->decompress();
+ img = decom;
+ }
+ alpha_cache.instantiate();
+ alpha_cache->create_from_image_alpha(img);
+ }
+ }
+
+ if (alpha_cache.is_valid()) {
+ int aw = int(alpha_cache->get_size().width);
+ int ah = int(alpha_cache->get_size().height);
+ if (aw == 0 || ah == 0) {
+ return true;
+ }
+
+ int x = p_x * aw / w;
+ int y = p_y * ah / h;
+
+ x = CLAMP(x, 0, aw);
+ y = CLAMP(y, 0, ah);
+
+ return alpha_cache->get_bit(x, y);
+ }
+
+ return true;
+}
+
+void ImageTexture::set_size_override(const Size2i &p_size) {
+ Size2i s = p_size;
+ if (s.x != 0) {
+ w = s.x;
+ }
+ if (s.y != 0) {
+ h = s.y;
+ }
+ RenderingServer::get_singleton()->texture_set_size_override(texture, w, h);
+}
+
+void ImageTexture::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+void ImageTexture::_bind_methods() {
+ ClassDB::bind_static_method("ImageTexture", D_METHOD("create_from_image", "image"), &ImageTexture::create_from_image);
+ ClassDB::bind_method(D_METHOD("get_format"), &ImageTexture::get_format);
+
+ ClassDB::bind_method(D_METHOD("set_image", "image"), &ImageTexture::set_image);
+ ClassDB::bind_method(D_METHOD("update", "image"), &ImageTexture::update);
+ ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override);
+}
+
+ImageTexture::ImageTexture() {}
+
+ImageTexture::~ImageTexture() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RenderingServer::get_singleton()->free(texture);
+ }
+}
+
+Image::Format ImageTextureLayered::get_format() const {
+ return format;
+}
+
+int ImageTextureLayered::get_width() const {
+ return width;
+}
+
+int ImageTextureLayered::get_height() const {
+ return height;
+}
+
+int ImageTextureLayered::get_layers() const {
+ return layers;
+}
+
+bool ImageTextureLayered::has_mipmaps() const {
+ return mipmaps;
+}
+
+ImageTextureLayered::LayeredType ImageTextureLayered::get_layered_type() const {
+ return layered_type;
+}
+
+Error ImageTextureLayered::_create_from_images(const TypedArray<Image> &p_images) {
+ Vector<Ref<Image>> images;
+ for (int i = 0; i < p_images.size(); i++) {
+ Ref<Image> img = p_images[i];
+ ERR_FAIL_COND_V(img.is_null(), ERR_INVALID_PARAMETER);
+ images.push_back(img);
+ }
+
+ return create_from_images(images);
+}
+
+TypedArray<Image> ImageTextureLayered::_get_images() const {
+ TypedArray<Image> images;
+ for (int i = 0; i < layers; i++) {
+ images.push_back(get_layer_data(i));
+ }
+ return images;
+}
+
+void ImageTextureLayered::_set_images(const TypedArray<Image> &p_images) {
+ ERR_FAIL_COND(_create_from_images(p_images) != OK);
+}
+
+Error ImageTextureLayered::create_from_images(Vector<Ref<Image>> p_images) {
+ int new_layers = p_images.size();
+ ERR_FAIL_COND_V(new_layers == 0, ERR_INVALID_PARAMETER);
+ if (layered_type == LAYERED_TYPE_CUBEMAP) {
+ ERR_FAIL_COND_V_MSG(new_layers != 6, ERR_INVALID_PARAMETER,
+ "Cubemaps require exactly 6 layers");
+ } else if (layered_type == LAYERED_TYPE_CUBEMAP_ARRAY) {
+ ERR_FAIL_COND_V_MSG((new_layers % 6) != 0, ERR_INVALID_PARAMETER,
+ "Cubemap array layers must be a multiple of 6");
+ }
+
+ ERR_FAIL_COND_V(p_images[0].is_null() || p_images[0]->is_empty(), ERR_INVALID_PARAMETER);
+
+ Image::Format new_format = p_images[0]->get_format();
+ int new_width = p_images[0]->get_width();
+ int new_height = p_images[0]->get_height();
+ bool new_mipmaps = p_images[0]->has_mipmaps();
+
+ for (int i = 1; i < p_images.size(); i++) {
+ ERR_FAIL_COND_V_MSG(p_images[i]->get_format() != new_format, ERR_INVALID_PARAMETER,
+ "All images must share the same format");
+ ERR_FAIL_COND_V_MSG(p_images[i]->get_width() != new_width || p_images[i]->get_height() != new_height, ERR_INVALID_PARAMETER,
+ "All images must share the same dimensions");
+ ERR_FAIL_COND_V_MSG(p_images[i]->has_mipmaps() != new_mipmaps, ERR_INVALID_PARAMETER,
+ "All images must share the usage of mipmaps");
+ }
+
+ if (texture.is_valid()) {
+ RID new_texture = RS::get_singleton()->texture_2d_layered_create(p_images, RS::TextureLayeredType(layered_type));
+ ERR_FAIL_COND_V(!new_texture.is_valid(), ERR_CANT_CREATE);
+ RS::get_singleton()->texture_replace(texture, new_texture);
+ } else {
+ texture = RS::get_singleton()->texture_2d_layered_create(p_images, RS::TextureLayeredType(layered_type));
+ ERR_FAIL_COND_V(!texture.is_valid(), ERR_CANT_CREATE);
+ }
+
+ format = new_format;
+ width = new_width;
+ height = new_height;
+ layers = new_layers;
+ mipmaps = new_mipmaps;
+ return OK;
+}
+
+void ImageTextureLayered::update_layer(const Ref<Image> &p_image, int p_layer) {
+ ERR_FAIL_COND_MSG(texture.is_null(), "Texture is not initialized.");
+ ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image.");
+ ERR_FAIL_COND_MSG(p_image->get_format() != format, "Image format must match texture's image format.");
+ ERR_FAIL_COND_MSG(p_image->get_width() != width || p_image->get_height() != height, "Image size must match texture's image size.");
+ ERR_FAIL_COND_MSG(p_image->has_mipmaps() != mipmaps, "Image mipmap configuration must match texture's image mipmap configuration.");
+ ERR_FAIL_INDEX_MSG(p_layer, layers, "Layer index is out of bounds.");
+ RS::get_singleton()->texture_2d_update(texture, p_image, p_layer);
+}
+
+Ref<Image> ImageTextureLayered::get_layer_data(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, layers, Ref<Image>());
+ return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
+}
+
+RID ImageTextureLayered::get_rid() const {
+ if (texture.is_null()) {
+ texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
+ }
+ return texture;
+}
+
+void ImageTextureLayered::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RS::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+void ImageTextureLayered::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create_from_images", "images"), &ImageTextureLayered::_create_from_images);
+ ClassDB::bind_method(D_METHOD("update_layer", "image", "layer"), &ImageTextureLayered::update_layer);
+
+ ClassDB::bind_method(D_METHOD("_get_images"), &ImageTextureLayered::_get_images);
+ ClassDB::bind_method(D_METHOD("_set_images", "images"), &ImageTextureLayered::_set_images);
+
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_images", PROPERTY_HINT_ARRAY_TYPE, "Image", PROPERTY_USAGE_INTERNAL), "_set_images", "_get_images");
+}
+
+ImageTextureLayered::ImageTextureLayered(LayeredType p_layered_type) {
+ layered_type = p_layered_type;
+}
+
+ImageTextureLayered::~ImageTextureLayered() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(texture);
+ }
+}
+
+Image::Format ImageTexture3D::get_format() const {
+ return format;
+}
+int ImageTexture3D::get_width() const {
+ return width;
+}
+int ImageTexture3D::get_height() const {
+ return height;
+}
+int ImageTexture3D::get_depth() const {
+ return depth;
+}
+bool ImageTexture3D::has_mipmaps() const {
+ return mipmaps;
+}
+
+Error ImageTexture3D::_create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const TypedArray<Image> &p_data) {
+ Vector<Ref<Image>> images;
+ images.resize(p_data.size());
+ for (int i = 0; i < images.size(); i++) {
+ images.write[i] = p_data[i];
+ }
+ return create(p_format, p_width, p_height, p_depth, p_mipmaps, images);
+}
+
+void ImageTexture3D::_update(const TypedArray<Image> &p_data) {
+ Vector<Ref<Image>> images;
+ images.resize(p_data.size());
+ for (int i = 0; i < images.size(); i++) {
+ images.write[i] = p_data[i];
+ }
+ return update(images);
+}
+
+Error ImageTexture3D::create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) {
+ RID tex = RenderingServer::get_singleton()->texture_3d_create(p_format, p_width, p_height, p_depth, p_mipmaps, p_data);
+ ERR_FAIL_COND_V(tex.is_null(), ERR_CANT_CREATE);
+
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_replace(texture, tex);
+ } else {
+ texture = tex;
+ }
+
+ format = p_format;
+ width = p_width;
+ height = p_height;
+ depth = p_depth;
+ mipmaps = p_mipmaps;
+
+ return OK;
+}
+
+void ImageTexture3D::update(const Vector<Ref<Image>> &p_data) {
+ ERR_FAIL_COND(!texture.is_valid());
+ RenderingServer::get_singleton()->texture_3d_update(texture, p_data);
+}
+
+Vector<Ref<Image>> ImageTexture3D::get_data() const {
+ ERR_FAIL_COND_V(!texture.is_valid(), Vector<Ref<Image>>());
+ return RS::get_singleton()->texture_3d_get(texture);
+}
+
+RID ImageTexture3D::get_rid() const {
+ if (!texture.is_valid()) {
+ texture = RS::get_singleton()->texture_3d_placeholder_create();
+ }
+ return texture;
+}
+void ImageTexture3D::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+void ImageTexture3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create", "format", "width", "height", "depth", "use_mipmaps", "data"), &ImageTexture3D::_create);
+ ClassDB::bind_method(D_METHOD("update", "data"), &ImageTexture3D::_update);
+}
+
+ImageTexture3D::ImageTexture3D() {
+}
+
+ImageTexture3D::~ImageTexture3D() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(texture);
+ }
+}
+
+void Texture2DArray::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create_placeholder"), &Texture2DArray::create_placeholder);
+}
+
+Ref<Resource> Texture2DArray::create_placeholder() const {
+ Ref<PlaceholderTexture2DArray> placeholder;
+ placeholder.instantiate();
+ placeholder->set_size(Size2i(get_width(), get_height()));
+ placeholder->set_layers(get_layers());
+ return placeholder;
+}
+
+void Cubemap::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create_placeholder"), &Cubemap::create_placeholder);
+}
+
+Ref<Resource> Cubemap::create_placeholder() const {
+ Ref<PlaceholderCubemap> placeholder;
+ placeholder.instantiate();
+ placeholder->set_size(Size2i(get_width(), get_height()));
+ placeholder->set_layers(get_layers());
+ return placeholder;
+}
+
+void CubemapArray::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create_placeholder"), &CubemapArray::create_placeholder);
+}
+
+Ref<Resource> CubemapArray::create_placeholder() const {
+ Ref<PlaceholderCubemapArray> placeholder;
+ placeholder.instantiate();
+ placeholder->set_size(Size2i(get_width(), get_height()));
+ placeholder->set_layers(get_layers());
+ return placeholder;
+}
diff --git a/scene/resources/image_texture.h b/scene/resources/image_texture.h
new file mode 100644
index 0000000000..9d9c296a45
--- /dev/null
+++ b/scene/resources/image_texture.h
@@ -0,0 +1,203 @@
+/**************************************************************************/
+/* image_texture.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 IMAGE_TEXTURE_H
+#define IMAGE_TEXTURE_H
+
+#include "scene/resources/texture.h"
+
+class BitMap;
+
+class ImageTexture : public Texture2D {
+ GDCLASS(ImageTexture, Texture2D);
+ RES_BASE_EXTENSION("tex");
+
+ mutable RID texture;
+ Image::Format format = Image::FORMAT_L8;
+ bool mipmaps = false;
+ int w = 0;
+ int h = 0;
+ Size2 size_override;
+ mutable Ref<BitMap> alpha_cache;
+ bool image_stored = false;
+
+protected:
+ virtual void reload_from_file() override;
+
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ static void _bind_methods();
+
+public:
+ void set_image(const Ref<Image> &p_image);
+ static Ref<ImageTexture> create_from_image(const Ref<Image> &p_image);
+
+ Image::Format get_format() const;
+
+ void update(const Ref<Image> &p_image);
+ Ref<Image> get_image() const override;
+
+ int get_width() const override;
+ int get_height() const override;
+
+ virtual RID get_rid() const override;
+
+ bool has_alpha() const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
+
+ bool is_pixel_opaque(int p_x, int p_y) const override;
+
+ void set_size_override(const Size2i &p_size);
+
+ virtual void set_path(const String &p_path, bool p_take_over = false) override;
+
+ ImageTexture();
+ ~ImageTexture();
+};
+
+class ImageTextureLayered : public TextureLayered {
+ GDCLASS(ImageTextureLayered, TextureLayered);
+
+ LayeredType layered_type;
+
+ mutable RID texture;
+ Image::Format format = Image::FORMAT_L8;
+
+ int width = 0;
+ int height = 0;
+ int layers = 0;
+ bool mipmaps = false;
+
+ Error _create_from_images(const TypedArray<Image> &p_images);
+
+ TypedArray<Image> _get_images() const;
+ void _set_images(const TypedArray<Image> &p_images);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Image::Format get_format() const override;
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual int get_layers() const override;
+ virtual bool has_mipmaps() const override;
+ virtual LayeredType get_layered_type() const override;
+
+ Error create_from_images(Vector<Ref<Image>> p_images);
+ void update_layer(const Ref<Image> &p_image, int p_layer);
+ virtual Ref<Image> get_layer_data(int p_layer) const override;
+
+ virtual RID get_rid() const override;
+ virtual void set_path(const String &p_path, bool p_take_over = false) override;
+
+ ImageTextureLayered(LayeredType p_layered_type);
+ ~ImageTextureLayered();
+};
+
+class ImageTexture3D : public Texture3D {
+ GDCLASS(ImageTexture3D, Texture3D);
+
+ mutable RID texture;
+
+ Image::Format format = Image::FORMAT_L8;
+ int width = 1;
+ int height = 1;
+ int depth = 1;
+ bool mipmaps = false;
+
+protected:
+ static void _bind_methods();
+
+ Error _create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const TypedArray<Image> &p_data);
+ void _update(const TypedArray<Image> &p_data);
+
+public:
+ virtual Image::Format get_format() const override;
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual int get_depth() const override;
+ virtual bool has_mipmaps() const override;
+
+ Error create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data);
+ void update(const Vector<Ref<Image>> &p_data);
+ virtual Vector<Ref<Image>> get_data() const override;
+
+ virtual RID get_rid() const override;
+ virtual void set_path(const String &p_path, bool p_take_over = false) override;
+
+ ImageTexture3D();
+ ~ImageTexture3D();
+};
+
+class Texture2DArray : public ImageTextureLayered {
+ GDCLASS(Texture2DArray, ImageTextureLayered)
+
+protected:
+ static void _bind_methods();
+
+public:
+ Texture2DArray() :
+ ImageTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
+
+ virtual Ref<Resource> create_placeholder() const;
+};
+
+class Cubemap : public ImageTextureLayered {
+ GDCLASS(Cubemap, ImageTextureLayered);
+
+protected:
+ static void _bind_methods();
+
+public:
+ Cubemap() :
+ ImageTextureLayered(LAYERED_TYPE_CUBEMAP) {}
+
+ virtual Ref<Resource> create_placeholder() const;
+};
+
+class CubemapArray : public ImageTextureLayered {
+ GDCLASS(CubemapArray, ImageTextureLayered);
+
+protected:
+ static void _bind_methods();
+
+public:
+ CubemapArray() :
+ ImageTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
+
+ virtual Ref<Resource> create_placeholder() const;
+};
+
+#endif // IMAGE_TEXTURE_H
diff --git a/scene/resources/label_settings.cpp b/scene/resources/label_settings.cpp
index 37912df921..9a530fb680 100644
--- a/scene/resources/label_settings.cpp
+++ b/scene/resources/label_settings.cpp
@@ -30,8 +30,6 @@
#include "label_settings.h"
-#include "core/core_string_names.h"
-
void LabelSettings::_font_changed() {
emit_changed();
}
@@ -95,11 +93,11 @@ real_t LabelSettings::get_line_spacing() const {
void LabelSettings::set_font(const Ref<Font> &p_font) {
if (font != p_font) {
if (font.is_valid()) {
- font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed));
+ font->disconnect_changed(callable_mp(this, &LabelSettings::_font_changed));
}
font = p_font;
if (font.is_valid()) {
- font->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &LabelSettings::_font_changed), CONNECT_REFERENCE_COUNTED);
+ font->connect_changed(callable_mp(this, &LabelSettings::_font_changed), CONNECT_REFERENCE_COUNTED);
}
emit_changed();
}
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index a2aab8e7b4..d3ef4a303b 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -385,7 +385,7 @@ void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) {
// This can be a slow operation, and `notify_property_list_changed()` (which is called by `_shader_changed()`)
// does nothing in non-editor builds anyway. See GH-34741 for details.
if (shader.is_valid() && Engine::get_singleton()->is_editor_hint()) {
- shader->disconnect("changed", callable_mp(this, &ShaderMaterial::_shader_changed));
+ shader->disconnect_changed(callable_mp(this, &ShaderMaterial::_shader_changed));
}
shader = p_shader;
@@ -395,7 +395,7 @@ void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) {
rid = shader->get_rid();
if (Engine::get_singleton()->is_editor_hint()) {
- shader->connect("changed", callable_mp(this, &ShaderMaterial::_shader_changed));
+ shader->connect_changed(callable_mp(this, &ShaderMaterial::_shader_changed));
}
}
diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp
index 015eb0fa45..5000541621 100644
--- a/scene/resources/mesh_library.cpp
+++ b/scene/resources/mesh_library.cpp
@@ -144,7 +144,6 @@ void MeshLibrary::set_item_name(int p_item, const String &p_name) {
void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].mesh = p_mesh;
- notify_change_to_owners();
emit_changed();
notify_property_list_changed();
}
@@ -152,7 +151,6 @@ void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) {
void MeshLibrary::set_item_mesh_transform(int p_item, const Transform3D &p_transform) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].mesh_transform = p_transform;
- notify_change_to_owners();
emit_changed();
}
@@ -160,7 +158,6 @@ void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes)
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].shapes = p_shapes;
notify_property_list_changed();
- notify_change_to_owners();
emit_changed();
notify_property_list_changed();
}
@@ -169,7 +166,6 @@ void MeshLibrary::set_item_navigation_mesh(int p_item, const Ref<NavigationMesh>
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].navigation_mesh = p_navigation_mesh;
notify_property_list_changed();
- notify_change_to_owners();
emit_changed();
notify_property_list_changed();
}
@@ -177,7 +173,6 @@ void MeshLibrary::set_item_navigation_mesh(int p_item, const Ref<NavigationMesh>
void MeshLibrary::set_item_navigation_mesh_transform(int p_item, const Transform3D &p_transform) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].navigation_mesh_transform = p_transform;
- notify_change_to_owners();
emit_changed();
notify_property_list_changed();
}
@@ -186,7 +181,6 @@ void MeshLibrary::set_item_navigation_layers(int p_item, uint32_t p_navigation_l
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].navigation_layers = p_navigation_layers;
notify_property_list_changed();
- notify_change_to_owners();
emit_changed();
}
@@ -244,14 +238,12 @@ bool MeshLibrary::has_item(int p_item) const {
void MeshLibrary::remove_item(int p_item) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map.erase(p_item);
- notify_change_to_owners();
notify_property_list_changed();
emit_changed();
}
void MeshLibrary::clear() {
item_map.clear();
- notify_change_to_owners();
notify_property_list_changed();
emit_changed();
}
diff --git a/scene/resources/mesh_texture.cpp b/scene/resources/mesh_texture.cpp
new file mode 100644
index 0000000000..1440b7f02b
--- /dev/null
+++ b/scene/resources/mesh_texture.cpp
@@ -0,0 +1,156 @@
+/**************************************************************************/
+/* mesh_texture.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 "mesh_texture.h"
+
+#include "scene/resources/mesh.h"
+
+int MeshTexture::get_width() const {
+ return size.width;
+}
+
+int MeshTexture::get_height() const {
+ return size.height;
+}
+
+RID MeshTexture::get_rid() const {
+ return RID();
+}
+
+bool MeshTexture::has_alpha() const {
+ return false;
+}
+
+void MeshTexture::set_mesh(const Ref<Mesh> &p_mesh) {
+ mesh = p_mesh;
+}
+
+Ref<Mesh> MeshTexture::get_mesh() const {
+ return mesh;
+}
+
+void MeshTexture::set_image_size(const Size2 &p_size) {
+ size = p_size;
+}
+
+Size2 MeshTexture::get_image_size() const {
+ return size;
+}
+
+void MeshTexture::set_base_texture(const Ref<Texture2D> &p_texture) {
+ base_texture = p_texture;
+}
+
+Ref<Texture2D> MeshTexture::get_base_texture() const {
+ return base_texture;
+}
+
+void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ if (mesh.is_null() || base_texture.is_null()) {
+ return;
+ }
+ Transform2D xform;
+ xform.set_origin(p_pos);
+ if (p_transpose) {
+ SWAP(xform.columns[0][1], xform.columns[1][0]);
+ SWAP(xform.columns[0][0], xform.columns[1][1]);
+ }
+ RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
+}
+
+void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ if (mesh.is_null() || base_texture.is_null()) {
+ return;
+ }
+ Transform2D xform;
+ Vector2 origin = p_rect.position;
+ if (p_rect.size.x < 0) {
+ origin.x += size.x;
+ }
+ if (p_rect.size.y < 0) {
+ origin.y += size.y;
+ }
+ xform.set_origin(origin);
+ xform.set_scale(p_rect.size / size);
+
+ if (p_transpose) {
+ SWAP(xform.columns[0][1], xform.columns[1][0]);
+ SWAP(xform.columns[0][0], xform.columns[1][1]);
+ }
+ RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
+}
+
+void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ if (mesh.is_null() || base_texture.is_null()) {
+ return;
+ }
+ Transform2D xform;
+ Vector2 origin = p_rect.position;
+ if (p_rect.size.x < 0) {
+ origin.x += size.x;
+ }
+ if (p_rect.size.y < 0) {
+ origin.y += size.y;
+ }
+ xform.set_origin(origin);
+ xform.set_scale(p_rect.size / size);
+
+ if (p_transpose) {
+ SWAP(xform.columns[0][1], xform.columns[1][0]);
+ SWAP(xform.columns[0][0], xform.columns[1][1]);
+ }
+ RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
+}
+
+bool MeshTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
+ r_rect = p_rect;
+ r_src_rect = p_src_rect;
+ return true;
+}
+
+bool MeshTexture::is_pixel_opaque(int p_x, int p_y) const {
+ return true;
+}
+
+void MeshTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &MeshTexture::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &MeshTexture::get_mesh);
+ ClassDB::bind_method(D_METHOD("set_image_size", "size"), &MeshTexture::set_image_size);
+ ClassDB::bind_method(D_METHOD("get_image_size"), &MeshTexture::get_image_size);
+ ClassDB::bind_method(D_METHOD("set_base_texture", "texture"), &MeshTexture::set_base_texture);
+ ClassDB::bind_method(D_METHOD("get_base_texture"), &MeshTexture::get_base_texture);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_base_texture", "get_base_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "image_size", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_image_size", "get_image_size");
+}
+
+MeshTexture::MeshTexture() {
+}
diff --git a/scene/resources/mesh_texture.h b/scene/resources/mesh_texture.h
new file mode 100644
index 0000000000..35ccb727c4
--- /dev/null
+++ b/scene/resources/mesh_texture.h
@@ -0,0 +1,75 @@
+/**************************************************************************/
+/* mesh_texture.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 MESH_TEXTURE_H
+#define MESH_TEXTURE_H
+
+#include "scene/resources/texture.h"
+
+class Mesh;
+
+class MeshTexture : public Texture2D {
+ GDCLASS(MeshTexture, Texture2D);
+ RES_BASE_EXTENSION("meshtex");
+
+ Ref<Texture2D> base_texture;
+ Ref<Mesh> mesh;
+ Size2i size;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual RID get_rid() const override;
+
+ virtual bool has_alpha() const override;
+
+ void set_mesh(const Ref<Mesh> &p_mesh);
+ Ref<Mesh> get_mesh() const;
+
+ void set_image_size(const Size2 &p_size);
+ Size2 get_image_size() const;
+
+ void set_base_texture(const Ref<Texture2D> &p_texture);
+ Ref<Texture2D> get_base_texture() const;
+
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
+ virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const override;
+
+ bool is_pixel_opaque(int p_x, int p_y) const override;
+
+ MeshTexture();
+};
+
+#endif // MESH_TEXTURE_H
diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp
index d6639d4e3f..745c71626c 100644
--- a/scene/resources/particle_process_material.cpp
+++ b/scene/resources/particle_process_material.cpp
@@ -31,6 +31,7 @@
#include "particle_process_material.h"
#include "core/version.h"
+#include "scene/resources/curve_texture.h"
Mutex ParticleProcessMaterial::material_mutex;
SelfList<ParticleProcessMaterial>::List *ParticleProcessMaterial::dirty_materials = nullptr;
diff --git a/scene/resources/placeholder_textures.cpp b/scene/resources/placeholder_textures.cpp
new file mode 100644
index 0000000000..224ea1f177
--- /dev/null
+++ b/scene/resources/placeholder_textures.cpp
@@ -0,0 +1,177 @@
+/**************************************************************************/
+/* placeholder_textures.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 "placeholder_textures.h"
+
+void PlaceholderTexture2D::set_size(Size2 p_size) {
+ size = p_size;
+}
+
+int PlaceholderTexture2D::get_width() const {
+ return size.width;
+}
+
+int PlaceholderTexture2D::get_height() const {
+ return size.height;
+}
+
+bool PlaceholderTexture2D::has_alpha() const {
+ return false;
+}
+
+Ref<Image> PlaceholderTexture2D::get_image() const {
+ return Ref<Image>();
+}
+
+RID PlaceholderTexture2D::get_rid() const {
+ return rid;
+}
+
+void PlaceholderTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture2D::set_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
+}
+
+PlaceholderTexture2D::PlaceholderTexture2D() {
+ rid = RS::get_singleton()->texture_2d_placeholder_create();
+}
+
+PlaceholderTexture2D::~PlaceholderTexture2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(rid);
+}
+
+///////////////////////////////////////////////
+
+void PlaceholderTexture3D::set_size(const Vector3i &p_size) {
+ size = p_size;
+}
+
+Vector3i PlaceholderTexture3D::get_size() const {
+ return size;
+}
+
+Image::Format PlaceholderTexture3D::get_format() const {
+ return Image::FORMAT_RGB8;
+}
+
+int PlaceholderTexture3D::get_width() const {
+ return size.x;
+}
+
+int PlaceholderTexture3D::get_height() const {
+ return size.y;
+}
+
+int PlaceholderTexture3D::get_depth() const {
+ return size.z;
+}
+
+bool PlaceholderTexture3D::has_mipmaps() const {
+ return false;
+}
+
+Vector<Ref<Image>> PlaceholderTexture3D::get_data() const {
+ return Vector<Ref<Image>>();
+}
+
+void PlaceholderTexture3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture3D::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTexture3D::get_size);
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
+}
+
+PlaceholderTexture3D::PlaceholderTexture3D() {
+ rid = RS::get_singleton()->texture_3d_placeholder_create();
+}
+PlaceholderTexture3D::~PlaceholderTexture3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(rid);
+}
+
+/////////////////////////////////////////////////
+
+void PlaceholderTextureLayered::set_size(const Size2i &p_size) {
+ size = p_size;
+}
+
+Size2i PlaceholderTextureLayered::get_size() const {
+ return size;
+}
+
+void PlaceholderTextureLayered::set_layers(int p_layers) {
+ layers = p_layers;
+}
+
+Image::Format PlaceholderTextureLayered::get_format() const {
+ return Image::FORMAT_RGB8;
+}
+
+TextureLayered::LayeredType PlaceholderTextureLayered::get_layered_type() const {
+ return layered_type;
+}
+
+int PlaceholderTextureLayered::get_width() const {
+ return size.x;
+}
+
+int PlaceholderTextureLayered::get_height() const {
+ return size.y;
+}
+
+int PlaceholderTextureLayered::get_layers() const {
+ return layers;
+}
+
+bool PlaceholderTextureLayered::has_mipmaps() const {
+ return false;
+}
+
+Ref<Image> PlaceholderTextureLayered::get_layer_data(int p_layer) const {
+ return Ref<Image>();
+}
+
+void PlaceholderTextureLayered::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTextureLayered::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTextureLayered::get_size);
+ ClassDB::bind_method(D_METHOD("set_layers", "layers"), &PlaceholderTextureLayered::set_layers);
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_RANGE, "1,4096"), "set_layers", "get_layers");
+}
+
+PlaceholderTextureLayered::PlaceholderTextureLayered(LayeredType p_type) {
+ layered_type = p_type;
+ rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
+}
+PlaceholderTextureLayered::~PlaceholderTextureLayered() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RS::get_singleton()->free(rid);
+}
diff --git a/scene/resources/placeholder_textures.h b/scene/resources/placeholder_textures.h
new file mode 100644
index 0000000000..116ed0f0f0
--- /dev/null
+++ b/scene/resources/placeholder_textures.h
@@ -0,0 +1,130 @@
+/**************************************************************************/
+/* placeholder_textures.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 PLACEHOLDER_TEXTURES_H
+#define PLACEHOLDER_TEXTURES_H
+
+#include "scene/resources/texture.h"
+
+class PlaceholderTexture2D : public Texture2D {
+ GDCLASS(PlaceholderTexture2D, Texture2D)
+
+ RID rid;
+ Size2 size = Size2(1, 1);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_size(Size2 p_size);
+
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual RID get_rid() const override;
+ virtual bool has_alpha() const override;
+
+ virtual Ref<Image> get_image() const override;
+
+ PlaceholderTexture2D();
+ ~PlaceholderTexture2D();
+};
+
+class PlaceholderTexture3D : public Texture3D {
+ GDCLASS(PlaceholderTexture3D, Texture3D)
+
+ RID rid;
+ Vector3i size = Vector3i(1, 1, 1);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_size(const Vector3i &p_size);
+ Vector3i get_size() const;
+ virtual Image::Format get_format() const override;
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual int get_depth() const override;
+ virtual bool has_mipmaps() const override;
+ virtual Vector<Ref<Image>> get_data() const override;
+
+ PlaceholderTexture3D();
+ ~PlaceholderTexture3D();
+};
+
+class PlaceholderTextureLayered : public TextureLayered {
+ GDCLASS(PlaceholderTextureLayered, TextureLayered)
+
+ RID rid;
+ Size2i size = Size2i(1, 1);
+ int layers = 1;
+ LayeredType layered_type = LAYERED_TYPE_2D_ARRAY;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_size(const Size2i &p_size);
+ Size2i get_size() const;
+ void set_layers(int p_layers);
+ virtual Image::Format get_format() const override;
+ virtual LayeredType get_layered_type() const override;
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+ virtual int get_layers() const override;
+ virtual bool has_mipmaps() const override;
+ virtual Ref<Image> get_layer_data(int p_layer) const override;
+
+ PlaceholderTextureLayered(LayeredType p_type);
+ ~PlaceholderTextureLayered();
+};
+
+class PlaceholderTexture2DArray : public PlaceholderTextureLayered {
+ GDCLASS(PlaceholderTexture2DArray, PlaceholderTextureLayered)
+public:
+ PlaceholderTexture2DArray() :
+ PlaceholderTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
+};
+
+class PlaceholderCubemap : public PlaceholderTextureLayered {
+ GDCLASS(PlaceholderCubemap, PlaceholderTextureLayered)
+public:
+ PlaceholderCubemap() :
+ PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP) {}
+};
+
+class PlaceholderCubemapArray : public PlaceholderTextureLayered {
+ GDCLASS(PlaceholderCubemapArray, PlaceholderTextureLayered)
+public:
+ PlaceholderCubemapArray() :
+ PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
+};
+
+#endif // PLACEHOLDER_TEXTURES_H
diff --git a/scene/resources/portable_compressed_texture.cpp b/scene/resources/portable_compressed_texture.cpp
new file mode 100644
index 0000000000..fc4e7823f0
--- /dev/null
+++ b/scene/resources/portable_compressed_texture.cpp
@@ -0,0 +1,339 @@
+/**************************************************************************/
+/* portable_compressed_texture.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 "portable_compressed_texture.h"
+
+#include "core/io/marshalls.h"
+#include "scene/resources/bit_map.h"
+
+void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
+ if (p_data.size() == 0) {
+ return; //nothing to do
+ }
+
+ const uint8_t *data = p_data.ptr();
+ uint32_t data_size = p_data.size();
+ ERR_FAIL_COND(data_size < 20);
+ compression_mode = CompressionMode(decode_uint32(data + 0));
+ format = Image::Format(decode_uint32(data + 4));
+ uint32_t mipmap_count = decode_uint32(data + 8);
+ size.width = decode_uint32(data + 12);
+ size.height = decode_uint32(data + 16);
+ mipmaps = mipmap_count > 1;
+
+ data += 20;
+ data_size -= 20;
+
+ Ref<Image> image;
+
+ switch (compression_mode) {
+ case COMPRESSION_MODE_LOSSLESS:
+ case COMPRESSION_MODE_LOSSY: {
+ Vector<uint8_t> image_data;
+
+ ERR_FAIL_COND(data_size < 4);
+ for (uint32_t i = 0; i < mipmap_count; i++) {
+ uint32_t mipsize = decode_uint32(data);
+ data += 4;
+ data_size -= 4;
+ ERR_FAIL_COND(mipsize < data_size);
+ Ref<Image> img = memnew(Image(data, data_size));
+ ERR_FAIL_COND(img->is_empty());
+ if (img->get_format() != format) { // May happen due to webp/png in the tiny mipmaps.
+ img->convert(format);
+ }
+ image_data.append_array(img->get_data());
+
+ data += mipsize;
+ data_size -= mipsize;
+ }
+
+ image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, image_data)));
+
+ } break;
+ case COMPRESSION_MODE_BASIS_UNIVERSAL: {
+ ERR_FAIL_NULL(Image::basis_universal_unpacker_ptr);
+ image = Image::basis_universal_unpacker_ptr(data, data_size);
+
+ } break;
+ case COMPRESSION_MODE_S3TC:
+ case COMPRESSION_MODE_ETC2:
+ case COMPRESSION_MODE_BPTC: {
+ image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, p_data.slice(20))));
+ } break;
+ }
+ ERR_FAIL_COND(image.is_null());
+
+ if (texture.is_null()) {
+ texture = RenderingServer::get_singleton()->texture_2d_create(image);
+ } else {
+ RID new_texture = RenderingServer::get_singleton()->texture_2d_create(image);
+ RenderingServer::get_singleton()->texture_replace(texture, new_texture);
+ }
+
+ image_stored = true;
+ RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
+ alpha_cache.unref();
+
+ if (keep_all_compressed_buffers || keep_compressed_buffer) {
+ compressed_buffer = p_data;
+ } else {
+ compressed_buffer.clear();
+ }
+}
+
+PortableCompressedTexture2D::CompressionMode PortableCompressedTexture2D::get_compression_mode() const {
+ return compression_mode;
+}
+Vector<uint8_t> PortableCompressedTexture2D::_get_data() const {
+ return compressed_buffer;
+}
+
+void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map, float p_lossy_quality) {
+ ERR_FAIL_COND(p_image.is_null() || p_image->is_empty());
+
+ Vector<uint8_t> buffer;
+
+ buffer.resize(20);
+ encode_uint32(p_compression_mode, buffer.ptrw());
+ encode_uint32(p_image->get_format(), buffer.ptrw() + 4);
+ encode_uint32(p_image->get_mipmap_count() + 1, buffer.ptrw() + 8);
+ encode_uint32(p_image->get_width(), buffer.ptrw() + 12);
+ encode_uint32(p_image->get_height(), buffer.ptrw() + 16);
+
+ switch (p_compression_mode) {
+ case COMPRESSION_MODE_LOSSLESS:
+ case COMPRESSION_MODE_LOSSY: {
+ for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
+ Vector<uint8_t> data;
+ if (p_compression_mode == COMPRESSION_MODE_LOSSY) {
+ data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality);
+ } else {
+ data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i));
+ }
+ int data_len = data.size();
+ buffer.resize(buffer.size() + 4);
+ encode_uint32(data_len, buffer.ptrw() + buffer.size() - 4);
+ buffer.append_array(data);
+ }
+ } break;
+ case COMPRESSION_MODE_BASIS_UNIVERSAL: {
+ Image::UsedChannels uc = p_image->detect_used_channels(p_normal_map ? Image::COMPRESS_SOURCE_NORMAL : Image::COMPRESS_SOURCE_GENERIC);
+ Vector<uint8_t> budata = Image::basis_universal_packer(p_image, uc);
+ buffer.append_array(budata);
+
+ } break;
+ case COMPRESSION_MODE_S3TC:
+ case COMPRESSION_MODE_ETC2:
+ case COMPRESSION_MODE_BPTC: {
+ Ref<Image> copy = p_image->duplicate();
+ switch (p_compression_mode) {
+ case COMPRESSION_MODE_S3TC:
+ copy->compress(Image::COMPRESS_S3TC);
+ break;
+ case COMPRESSION_MODE_ETC2:
+ copy->compress(Image::COMPRESS_ETC2);
+ break;
+ case COMPRESSION_MODE_BPTC:
+ copy->compress(Image::COMPRESS_BPTC);
+ break;
+ default: {
+ };
+ }
+
+ buffer.append_array(copy->get_data());
+
+ } break;
+ }
+
+ _set_data(buffer);
+}
+
+Image::Format PortableCompressedTexture2D::get_format() const {
+ return format;
+}
+
+Ref<Image> PortableCompressedTexture2D::get_image() const {
+ if (image_stored) {
+ return RenderingServer::get_singleton()->texture_2d_get(texture);
+ } else {
+ return Ref<Image>();
+ }
+}
+
+int PortableCompressedTexture2D::get_width() const {
+ return size.width;
+}
+
+int PortableCompressedTexture2D::get_height() const {
+ return size.height;
+}
+
+RID PortableCompressedTexture2D::get_rid() const {
+ if (texture.is_null()) {
+ //we are in trouble, create something temporary
+ texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+ return texture;
+}
+
+bool PortableCompressedTexture2D::has_alpha() const {
+ return (format == Image::FORMAT_LA8 || format == Image::FORMAT_RGBA8);
+}
+
+void PortableCompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ if (size.width == 0 || size.height == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, size), texture, false, p_modulate, p_transpose);
+}
+
+void PortableCompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ if (size.width == 0 || size.height == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
+}
+
+void PortableCompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ if (size.width == 0 || size.height == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
+}
+
+bool PortableCompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
+ if (!alpha_cache.is_valid()) {
+ Ref<Image> img = get_image();
+ if (img.is_valid()) {
+ if (img->is_compressed()) { //must decompress, if compressed
+ Ref<Image> decom = img->duplicate();
+ decom->decompress();
+ img = decom;
+ }
+ alpha_cache.instantiate();
+ alpha_cache->create_from_image_alpha(img);
+ }
+ }
+
+ if (alpha_cache.is_valid()) {
+ int aw = int(alpha_cache->get_size().width);
+ int ah = int(alpha_cache->get_size().height);
+ if (aw == 0 || ah == 0) {
+ return true;
+ }
+
+ int x = p_x * aw / size.width;
+ int y = p_y * ah / size.height;
+
+ x = CLAMP(x, 0, aw);
+ y = CLAMP(y, 0, ah);
+
+ return alpha_cache->get_bit(x, y);
+ }
+
+ return true;
+}
+
+void PortableCompressedTexture2D::set_size_override(const Size2 &p_size) {
+ size_override = p_size;
+ RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
+}
+
+Size2 PortableCompressedTexture2D::get_size_override() const {
+ return size_override;
+}
+
+void PortableCompressedTexture2D::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+bool PortableCompressedTexture2D::keep_all_compressed_buffers = false;
+
+void PortableCompressedTexture2D::set_keep_all_compressed_buffers(bool p_keep) {
+ keep_all_compressed_buffers = p_keep;
+}
+
+bool PortableCompressedTexture2D::is_keeping_all_compressed_buffers() {
+ return keep_all_compressed_buffers;
+}
+
+void PortableCompressedTexture2D::set_keep_compressed_buffer(bool p_keep) {
+ keep_compressed_buffer = p_keep;
+ if (!p_keep) {
+ compressed_buffer.clear();
+ }
+}
+
+bool PortableCompressedTexture2D::is_keeping_compressed_buffer() const {
+ return keep_compressed_buffer;
+}
+
+void PortableCompressedTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create_from_image", "image", "compression_mode", "normal_map", "lossy_quality"), &PortableCompressedTexture2D::create_from_image, DEFVAL(false), DEFVAL(0.8));
+ ClassDB::bind_method(D_METHOD("get_format"), &PortableCompressedTexture2D::get_format);
+ ClassDB::bind_method(D_METHOD("get_compression_mode"), &PortableCompressedTexture2D::get_compression_mode);
+
+ ClassDB::bind_method(D_METHOD("set_size_override", "size"), &PortableCompressedTexture2D::set_size_override);
+ ClassDB::bind_method(D_METHOD("get_size_override"), &PortableCompressedTexture2D::get_size_override);
+
+ ClassDB::bind_method(D_METHOD("set_keep_compressed_buffer", "keep"), &PortableCompressedTexture2D::set_keep_compressed_buffer);
+ ClassDB::bind_method(D_METHOD("is_keeping_compressed_buffer"), &PortableCompressedTexture2D::is_keeping_compressed_buffer);
+
+ ClassDB::bind_method(D_METHOD("_set_data", "data"), &PortableCompressedTexture2D::_set_data);
+ ClassDB::bind_method(D_METHOD("_get_data"), &PortableCompressedTexture2D::_get_data);
+
+ ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("set_keep_all_compressed_buffers", "keep"), &PortableCompressedTexture2D::set_keep_all_compressed_buffers);
+ ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("is_keeping_all_compressed_buffers"), &PortableCompressedTexture2D::is_keeping_all_compressed_buffers);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size_override", PROPERTY_HINT_NONE, "suffix:px"), "set_size_override", "get_size_override");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_compressed_buffer"), "set_keep_compressed_buffer", "is_keeping_compressed_buffer");
+
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSLESS);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSY);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_BASIS_UNIVERSAL);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_S3TC);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_ETC2);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_BPTC);
+}
+
+PortableCompressedTexture2D::PortableCompressedTexture2D() {}
+
+PortableCompressedTexture2D::~PortableCompressedTexture2D() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RenderingServer::get_singleton()->free(texture);
+ }
+}
diff --git a/scene/resources/portable_compressed_texture.h b/scene/resources/portable_compressed_texture.h
new file mode 100644
index 0000000000..86d80e39f7
--- /dev/null
+++ b/scene/resources/portable_compressed_texture.h
@@ -0,0 +1,110 @@
+/**************************************************************************/
+/* portable_compressed_texture.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 PORTABLE_COMPRESSED_TEXTURE_H
+#define PORTABLE_COMPRESSED_TEXTURE_H
+
+#include "scene/resources/texture.h"
+
+class BitMap;
+
+class PortableCompressedTexture2D : public Texture2D {
+ GDCLASS(PortableCompressedTexture2D, Texture2D);
+
+public:
+ enum CompressionMode {
+ COMPRESSION_MODE_LOSSLESS,
+ COMPRESSION_MODE_LOSSY,
+ COMPRESSION_MODE_BASIS_UNIVERSAL,
+ COMPRESSION_MODE_S3TC,
+ COMPRESSION_MODE_ETC2,
+ COMPRESSION_MODE_BPTC,
+ };
+
+private:
+ CompressionMode compression_mode = COMPRESSION_MODE_LOSSLESS;
+ static bool keep_all_compressed_buffers;
+ bool keep_compressed_buffer = false;
+ Vector<uint8_t> compressed_buffer;
+ Size2 size;
+ Size2 size_override;
+ bool mipmaps = false;
+ Image::Format format = Image::FORMAT_L8;
+
+ mutable RID texture;
+ mutable Ref<BitMap> alpha_cache;
+
+ bool image_stored = false;
+
+protected:
+ Vector<uint8_t> _get_data() const;
+ void _set_data(const Vector<uint8_t> &p_data);
+
+ static void _bind_methods();
+
+public:
+ CompressionMode get_compression_mode() const;
+ void create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map = false, float p_lossy_quality = 0.8);
+
+ Image::Format get_format() const;
+
+ void update(const Ref<Image> &p_image);
+ Ref<Image> get_image() const override;
+
+ int get_width() const override;
+ int get_height() const override;
+
+ virtual RID get_rid() const override;
+
+ bool has_alpha() const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
+
+ bool is_pixel_opaque(int p_x, int p_y) const override;
+
+ virtual void set_path(const String &p_path, bool p_take_over = false) override;
+
+ void set_size_override(const Size2 &p_size);
+ Size2 get_size_override() const;
+
+ void set_keep_compressed_buffer(bool p_keep);
+ bool is_keeping_compressed_buffer() const;
+
+ static void set_keep_all_compressed_buffers(bool p_keep);
+ static bool is_keeping_all_compressed_buffers();
+
+ PortableCompressedTexture2D();
+ ~PortableCompressedTexture2D();
+};
+
+VARIANT_ENUM_CAST(PortableCompressedTexture2D::CompressionMode)
+
+#endif // PORTABLE_COMPRESSED_TEXTURE_H
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 204987cac9..6430a1302d 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -31,7 +31,6 @@
#include "primitive_meshes.h"
#include "core/config/project_settings.h"
-#include "core/core_string_names.h"
#include "scene/resources/theme.h"
#include "scene/theme/theme_db.h"
#include "servers/rendering_server.h"
@@ -2194,11 +2193,11 @@ void TubeTrailMesh::set_curve(const Ref<Curve> &p_curve) {
return;
}
if (curve.is_valid()) {
- curve->disconnect("changed", callable_mp(this, &TubeTrailMesh::_curve_changed));
+ curve->disconnect_changed(callable_mp(this, &TubeTrailMesh::_curve_changed));
}
curve = p_curve;
if (curve.is_valid()) {
- curve->connect("changed", callable_mp(this, &TubeTrailMesh::_curve_changed));
+ curve->connect_changed(callable_mp(this, &TubeTrailMesh::_curve_changed));
}
_request_update();
}
@@ -2533,11 +2532,11 @@ void RibbonTrailMesh::set_curve(const Ref<Curve> &p_curve) {
return;
}
if (curve.is_valid()) {
- curve->disconnect("changed", callable_mp(this, &RibbonTrailMesh::_curve_changed));
+ curve->disconnect_changed(callable_mp(this, &RibbonTrailMesh::_curve_changed));
}
curve = p_curve;
if (curve.is_valid()) {
- curve->connect("changed", callable_mp(this, &RibbonTrailMesh::_curve_changed));
+ curve->connect_changed(callable_mp(this, &RibbonTrailMesh::_curve_changed));
}
_request_update();
}
@@ -3446,13 +3445,13 @@ void TextMesh::_font_changed() {
void TextMesh::set_font(const Ref<Font> &p_font) {
if (font_override != p_font) {
if (font_override.is_valid()) {
- font_override->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed"));
+ font_override->disconnect_changed(Callable(this, "_font_changed"));
}
font_override = p_font;
dirty_font = true;
dirty_cache = true;
if (font_override.is_valid()) {
- font_override->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed"));
+ font_override->connect_changed(Callable(this, "_font_changed"));
}
_request_update();
}
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index dc0240859e..c02431efa8 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1877,6 +1877,8 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
return;
}
+ resource_set.insert(res);
+
List<PropertyInfo> property_list;
res->get_property_list(&property_list);
@@ -1908,8 +1910,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
I = I->next();
}
- resource_set.insert(res); //saved after, so the children it needs are available when loaded
- saved_resources.push_back(res);
+ saved_resources.push_back(res); // Saved after, so the children it needs are available when loaded
} break;
case Variant::ARRAY: {
diff --git a/scene/resources/separation_ray_shape_3d.cpp b/scene/resources/separation_ray_shape_3d.cpp
index 3d8e2af201..07e93b8b79 100644
--- a/scene/resources/separation_ray_shape_3d.cpp
+++ b/scene/resources/separation_ray_shape_3d.cpp
@@ -56,7 +56,7 @@ void SeparationRayShape3D::_update_shape() {
void SeparationRayShape3D::set_length(float p_length) {
length = p_length;
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
float SeparationRayShape3D::get_length() const {
@@ -66,7 +66,7 @@ float SeparationRayShape3D::get_length() const {
void SeparationRayShape3D::set_slide_on_slope(bool p_active) {
slide_on_slope = p_active;
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
bool SeparationRayShape3D::get_slide_on_slope() const {
@@ -88,5 +88,5 @@ SeparationRayShape3D::SeparationRayShape3D() :
Shape3D(PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_SEPARATION_RAY)) {
/* Code copied from setters to prevent the use of uninitialized variables */
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 62c2483a93..f919386980 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -63,21 +63,7 @@ void Shader::set_include_path(const String &p_path) {
void Shader::set_code(const String &p_code) {
for (const Ref<ShaderInclude> &E : include_dependencies) {
- E->disconnect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed));
- }
-
- String type = ShaderLanguage::get_shader_type(p_code);
-
- if (type == "canvas_item") {
- mode = MODE_CANVAS_ITEM;
- } else if (type == "particles") {
- mode = MODE_PARTICLES;
- } else if (type == "sky") {
- mode = MODE_SKY;
- } else if (type == "fog") {
- mode = MODE_FOG;
- } else {
- mode = MODE_SPATIAL;
+ E->disconnect_changed(callable_mp(this, &Shader::_dependency_changed));
}
code = p_code;
@@ -100,8 +86,23 @@ void Shader::set_code(const String &p_code) {
}
}
+ // Try to get the shader type from the final, fully preprocessed shader code.
+ String type = ShaderLanguage::get_shader_type(pp_code);
+
+ if (type == "canvas_item") {
+ mode = MODE_CANVAS_ITEM;
+ } else if (type == "particles") {
+ mode = MODE_PARTICLES;
+ } else if (type == "sky") {
+ mode = MODE_SKY;
+ } else if (type == "fog") {
+ mode = MODE_FOG;
+ } else {
+ mode = MODE_SPATIAL;
+ }
+
for (const Ref<ShaderInclude> &E : include_dependencies) {
- E->connect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed));
+ E->connect_changed(callable_mp(this, &Shader::_dependency_changed));
}
RenderingServer::get_singleton()->shader_set_code(shader, pp_code);
diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp
index 6e9c64d34b..aead484cc1 100644
--- a/scene/resources/shader_include.cpp
+++ b/scene/resources/shader_include.cpp
@@ -40,7 +40,7 @@ void ShaderInclude::set_code(const String &p_code) {
code = p_code;
for (const Ref<ShaderInclude> &E : dependencies) {
- E->disconnect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed));
+ E->disconnect_changed(callable_mp(this, &ShaderInclude::_dependency_changed));
}
{
@@ -60,7 +60,7 @@ void ShaderInclude::set_code(const String &p_code) {
}
for (const Ref<ShaderInclude> &E : dependencies) {
- E->connect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed));
+ E->connect_changed(callable_mp(this, &ShaderInclude::_dependency_changed));
}
emit_changed();
diff --git a/scene/resources/sphere_shape_3d.cpp b/scene/resources/sphere_shape_3d.cpp
index fa22aabe5b..56b78471ec 100644
--- a/scene/resources/sphere_shape_3d.cpp
+++ b/scene/resources/sphere_shape_3d.cpp
@@ -67,7 +67,7 @@ void SphereShape3D::set_radius(float p_radius) {
ERR_FAIL_COND_MSG(p_radius < 0, "SphereShape3D radius cannot be negative.");
radius = p_radius;
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
float SphereShape3D::get_radius() const {
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index a9ebb21345..f87bf1ee05 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -32,8 +32,6 @@
#include "scene/main/canvas_item.h"
-#include <limits.h>
-
Size2 StyleBox::get_minimum_size() const {
Size2 min_size = Size2(get_margin(SIDE_LEFT) + get_margin(SIDE_RIGHT), get_margin(SIDE_TOP) + get_margin(SIDE_BOTTOM));
Size2 custom_size;
@@ -145,925 +143,3 @@ StyleBox::StyleBox() {
content_margin[i] = -1;
}
}
-
-void StyleBoxTexture::set_texture(Ref<Texture2D> p_texture) {
- if (texture == p_texture) {
- return;
- }
- texture = p_texture;
- emit_changed();
-}
-
-Ref<Texture2D> StyleBoxTexture::get_texture() const {
- return texture;
-}
-
-void StyleBoxTexture::set_texture_margin(Side p_side, float p_size) {
- ERR_FAIL_INDEX((int)p_side, 4);
-
- texture_margin[p_side] = p_size;
- emit_changed();
-}
-
-void StyleBoxTexture::set_texture_margin_all(float p_size) {
- for (int i = 0; i < 4; i++) {
- texture_margin[i] = p_size;
- }
- emit_changed();
-}
-
-void StyleBoxTexture::set_texture_margin_individual(float p_left, float p_top, float p_right, float p_bottom) {
- texture_margin[SIDE_LEFT] = p_left;
- texture_margin[SIDE_TOP] = p_top;
- texture_margin[SIDE_RIGHT] = p_right;
- texture_margin[SIDE_BOTTOM] = p_bottom;
- emit_changed();
-}
-
-float StyleBoxTexture::get_texture_margin(Side p_side) const {
- ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
-
- return texture_margin[p_side];
-}
-
-float StyleBoxTexture::get_style_margin(Side p_side) const {
- ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
-
- return texture_margin[p_side];
-}
-
-Rect2 StyleBoxTexture::get_draw_rect(const Rect2 &p_rect) const {
- return p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]);
-}
-
-void StyleBoxTexture::draw(RID p_canvas_item, const Rect2 &p_rect) const {
- if (texture.is_null()) {
- return;
- }
-
- Rect2 rect = p_rect;
- Rect2 src_rect = region_rect;
-
- texture->get_rect_region(rect, src_rect, rect, src_rect);
-
- rect.position.x -= expand_margin[SIDE_LEFT];
- rect.position.y -= expand_margin[SIDE_TOP];
- rect.size.x += expand_margin[SIDE_LEFT] + expand_margin[SIDE_RIGHT];
- rect.size.y += expand_margin[SIDE_TOP] + expand_margin[SIDE_BOTTOM];
-
- Vector2 start_offset = Vector2(texture_margin[SIDE_LEFT], texture_margin[SIDE_TOP]);
- Vector2 end_offset = Vector2(texture_margin[SIDE_RIGHT], texture_margin[SIDE_BOTTOM]);
-
- RenderingServer::get_singleton()->canvas_item_add_nine_patch(p_canvas_item, rect, src_rect, texture->get_rid(), start_offset, end_offset, RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center, modulate);
-}
-
-void StyleBoxTexture::set_draw_center(bool p_enabled) {
- draw_center = p_enabled;
- emit_changed();
-}
-
-bool StyleBoxTexture::is_draw_center_enabled() const {
- return draw_center;
-}
-
-void StyleBoxTexture::set_expand_margin(Side p_side, float p_size) {
- ERR_FAIL_INDEX((int)p_side, 4);
- expand_margin[p_side] = p_size;
- emit_changed();
-}
-
-void StyleBoxTexture::set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom) {
- expand_margin[SIDE_LEFT] = p_left;
- expand_margin[SIDE_TOP] = p_top;
- expand_margin[SIDE_RIGHT] = p_right;
- expand_margin[SIDE_BOTTOM] = p_bottom;
- emit_changed();
-}
-
-void StyleBoxTexture::set_expand_margin_all(float p_expand_margin_size) {
- for (int i = 0; i < 4; i++) {
- expand_margin[i] = p_expand_margin_size;
- }
- emit_changed();
-}
-
-float StyleBoxTexture::get_expand_margin(Side p_side) const {
- ERR_FAIL_INDEX_V((int)p_side, 4, 0);
- return expand_margin[p_side];
-}
-
-void StyleBoxTexture::set_region_rect(const Rect2 &p_region_rect) {
- if (region_rect == p_region_rect) {
- return;
- }
-
- region_rect = p_region_rect;
- emit_changed();
-}
-
-Rect2 StyleBoxTexture::get_region_rect() const {
- return region_rect;
-}
-
-void StyleBoxTexture::set_h_axis_stretch_mode(AxisStretchMode p_mode) {
- ERR_FAIL_INDEX((int)p_mode, 3);
- axis_h = p_mode;
- emit_changed();
-}
-
-StyleBoxTexture::AxisStretchMode StyleBoxTexture::get_h_axis_stretch_mode() const {
- return axis_h;
-}
-
-void StyleBoxTexture::set_v_axis_stretch_mode(AxisStretchMode p_mode) {
- ERR_FAIL_INDEX((int)p_mode, 3);
- axis_v = p_mode;
- emit_changed();
-}
-
-StyleBoxTexture::AxisStretchMode StyleBoxTexture::get_v_axis_stretch_mode() const {
- return axis_v;
-}
-
-void StyleBoxTexture::set_modulate(const Color &p_modulate) {
- if (modulate == p_modulate) {
- return;
- }
- modulate = p_modulate;
- emit_changed();
-}
-
-Color StyleBoxTexture::get_modulate() const {
- return modulate;
-}
-
-void StyleBoxTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_texture", "texture"), &StyleBoxTexture::set_texture);
- ClassDB::bind_method(D_METHOD("get_texture"), &StyleBoxTexture::get_texture);
-
- ClassDB::bind_method(D_METHOD("set_texture_margin", "margin", "size"), &StyleBoxTexture::set_texture_margin);
- ClassDB::bind_method(D_METHOD("set_texture_margin_all", "size"), &StyleBoxTexture::set_texture_margin_all);
- ClassDB::bind_method(D_METHOD("get_texture_margin", "margin"), &StyleBoxTexture::get_texture_margin);
-
- ClassDB::bind_method(D_METHOD("set_expand_margin", "margin", "size"), &StyleBoxTexture::set_expand_margin);
- ClassDB::bind_method(D_METHOD("set_expand_margin_all", "size"), &StyleBoxTexture::set_expand_margin_all);
- ClassDB::bind_method(D_METHOD("get_expand_margin", "margin"), &StyleBoxTexture::get_expand_margin);
-
- ClassDB::bind_method(D_METHOD("set_region_rect", "region"), &StyleBoxTexture::set_region_rect);
- ClassDB::bind_method(D_METHOD("get_region_rect"), &StyleBoxTexture::get_region_rect);
-
- ClassDB::bind_method(D_METHOD("set_draw_center", "enable"), &StyleBoxTexture::set_draw_center);
- ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxTexture::is_draw_center_enabled);
-
- ClassDB::bind_method(D_METHOD("set_modulate", "color"), &StyleBoxTexture::set_modulate);
- ClassDB::bind_method(D_METHOD("get_modulate"), &StyleBoxTexture::get_modulate);
-
- ClassDB::bind_method(D_METHOD("set_h_axis_stretch_mode", "mode"), &StyleBoxTexture::set_h_axis_stretch_mode);
- ClassDB::bind_method(D_METHOD("get_h_axis_stretch_mode"), &StyleBoxTexture::get_h_axis_stretch_mode);
-
- ClassDB::bind_method(D_METHOD("set_v_axis_stretch_mode", "mode"), &StyleBoxTexture::set_v_axis_stretch_mode);
- ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &StyleBoxTexture::get_v_axis_stretch_mode);
-
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
-
- ADD_GROUP("Texture Margins", "texture_margin_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_BOTTOM);
-
- ADD_GROUP("Expand Margins", "expand_margin_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM);
-
- ADD_GROUP("Axis Stretch", "axis_stretch_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
-
- ADD_GROUP("Sub-Region", "region_");
- ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect", PROPERTY_HINT_NONE, "suffix:px"), "set_region_rect", "get_region_rect");
-
- ADD_GROUP("Modulate", "modulate_");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate_color"), "set_modulate", "get_modulate");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled");
-
- BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_STRETCH);
- BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_TILE);
- BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_TILE_FIT);
-}
-
-StyleBoxTexture::StyleBoxTexture() {}
-
-StyleBoxTexture::~StyleBoxTexture() {}
-
-////////////////
-
-void StyleBoxFlat::set_bg_color(const Color &p_color) {
- bg_color = p_color;
- emit_changed();
-}
-
-Color StyleBoxFlat::get_bg_color() const {
- return bg_color;
-}
-
-void StyleBoxFlat::set_border_color(const Color &p_color) {
- border_color = p_color;
- emit_changed();
-}
-
-Color StyleBoxFlat::get_border_color() const {
- return border_color;
-}
-
-void StyleBoxFlat::set_border_width_all(int p_size) {
- border_width[0] = p_size;
- border_width[1] = p_size;
- border_width[2] = p_size;
- border_width[3] = p_size;
- emit_changed();
-}
-
-int StyleBoxFlat::get_border_width_min() const {
- return MIN(MIN(border_width[0], border_width[1]), MIN(border_width[2], border_width[3]));
-}
-
-void StyleBoxFlat::set_border_width(Side p_side, int p_width) {
- ERR_FAIL_INDEX((int)p_side, 4);
- border_width[p_side] = p_width;
- emit_changed();
-}
-
-int StyleBoxFlat::get_border_width(Side p_side) const {
- ERR_FAIL_INDEX_V((int)p_side, 4, 0);
- return border_width[p_side];
-}
-
-void StyleBoxFlat::set_border_blend(bool p_blend) {
- blend_border = p_blend;
- emit_changed();
-}
-
-bool StyleBoxFlat::get_border_blend() const {
- return blend_border;
-}
-
-void StyleBoxFlat::set_corner_radius_all(int radius) {
- for (int i = 0; i < 4; i++) {
- corner_radius[i] = radius;
- }
-
- emit_changed();
-}
-
-void StyleBoxFlat::set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left) {
- corner_radius[0] = radius_top_left;
- corner_radius[1] = radius_top_right;
- corner_radius[2] = radius_bottom_right;
- corner_radius[3] = radius_bottom_left;
-
- emit_changed();
-}
-
-void StyleBoxFlat::set_corner_radius(const Corner p_corner, const int radius) {
- ERR_FAIL_INDEX((int)p_corner, 4);
- corner_radius[p_corner] = radius;
- emit_changed();
-}
-
-int StyleBoxFlat::get_corner_radius(const Corner p_corner) const {
- ERR_FAIL_INDEX_V((int)p_corner, 4, 0);
- return corner_radius[p_corner];
-}
-
-void StyleBoxFlat::set_expand_margin(Side p_side, float p_size) {
- ERR_FAIL_INDEX((int)p_side, 4);
- expand_margin[p_side] = p_size;
- emit_changed();
-}
-
-void StyleBoxFlat::set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom) {
- expand_margin[SIDE_LEFT] = p_left;
- expand_margin[SIDE_TOP] = p_top;
- expand_margin[SIDE_RIGHT] = p_right;
- expand_margin[SIDE_BOTTOM] = p_bottom;
- emit_changed();
-}
-
-void StyleBoxFlat::set_expand_margin_all(float p_expand_margin_size) {
- for (int i = 0; i < 4; i++) {
- expand_margin[i] = p_expand_margin_size;
- }
- emit_changed();
-}
-
-float StyleBoxFlat::get_expand_margin(Side p_side) const {
- ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
- return expand_margin[p_side];
-}
-
-void StyleBoxFlat::set_draw_center(bool p_enabled) {
- draw_center = p_enabled;
- emit_changed();
-}
-
-bool StyleBoxFlat::is_draw_center_enabled() const {
- return draw_center;
-}
-
-void StyleBoxFlat::set_skew(Vector2 p_skew) {
- skew = p_skew;
- emit_changed();
-}
-
-Vector2 StyleBoxFlat::get_skew() const {
- return skew;
-}
-
-void StyleBoxFlat::set_shadow_color(const Color &p_color) {
- shadow_color = p_color;
- emit_changed();
-}
-
-Color StyleBoxFlat::get_shadow_color() const {
- return shadow_color;
-}
-
-void StyleBoxFlat::set_shadow_size(const int &p_size) {
- shadow_size = p_size;
- emit_changed();
-}
-
-int StyleBoxFlat::get_shadow_size() const {
- return shadow_size;
-}
-
-void StyleBoxFlat::set_shadow_offset(const Point2 &p_offset) {
- shadow_offset = p_offset;
- emit_changed();
-}
-
-Point2 StyleBoxFlat::get_shadow_offset() const {
- return shadow_offset;
-}
-
-void StyleBoxFlat::set_anti_aliased(const bool &p_anti_aliased) {
- anti_aliased = p_anti_aliased;
- emit_changed();
- notify_property_list_changed();
-}
-
-bool StyleBoxFlat::is_anti_aliased() const {
- return anti_aliased;
-}
-
-void StyleBoxFlat::set_aa_size(const real_t p_aa_size) {
- aa_size = CLAMP(p_aa_size, 0.01, 10);
- emit_changed();
-}
-
-real_t StyleBoxFlat::get_aa_size() const {
- return aa_size;
-}
-
-void StyleBoxFlat::set_corner_detail(const int &p_corner_detail) {
- corner_detail = CLAMP(p_corner_detail, 1, 20);
- emit_changed();
-}
-
-int StyleBoxFlat::get_corner_detail() const {
- return corner_detail;
-}
-
-inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_rect, const real_t corner_radius[4], real_t *inner_corner_radius) {
- real_t border_left = inner_rect.position.x - style_rect.position.x;
- real_t border_top = inner_rect.position.y - style_rect.position.y;
- real_t border_right = style_rect.size.width - inner_rect.size.width - border_left;
- real_t border_bottom = style_rect.size.height - inner_rect.size.height - border_top;
-
- real_t rad;
-
- // Top left.
- rad = MIN(border_top, border_left);
- inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0);
-
- // Top right;
- rad = MIN(border_top, border_right);
- inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0);
-
- // Bottom right.
- rad = MIN(border_bottom, border_right);
- inner_corner_radius[2] = MAX(corner_radius[2] - rad, 0);
-
- // Bottom left.
- rad = MIN(border_bottom, border_left);
- inner_corner_radius[3] = MAX(corner_radius[3] - rad, 0);
-}
-
-inline void draw_rounded_rectangle(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4],
- const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const Vector2 &skew, bool is_filled = false) {
- int vert_offset = verts.size();
- if (!vert_offset) {
- vert_offset = 0;
- }
-
- int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail;
-
- bool draw_border = !is_filled;
-
- real_t ring_corner_radius[4];
- set_inner_corner_radius(style_rect, ring_rect, corner_radius, ring_corner_radius);
-
- // Corner radius center points.
- Vector<Point2> outer_points = {
- ring_rect.position + Vector2(ring_corner_radius[0], ring_corner_radius[0]), //tl
- Point2(ring_rect.position.x + ring_rect.size.x - ring_corner_radius[1], ring_rect.position.y + ring_corner_radius[1]), //tr
- ring_rect.position + ring_rect.size - Vector2(ring_corner_radius[2], ring_corner_radius[2]), //br
- Point2(ring_rect.position.x + ring_corner_radius[3], ring_rect.position.y + ring_rect.size.y - ring_corner_radius[3]) //bl
- };
-
- real_t inner_corner_radius[4];
- set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius);
-
- Vector<Point2> inner_points = {
- inner_rect.position + Vector2(inner_corner_radius[0], inner_corner_radius[0]), //tl
- Point2(inner_rect.position.x + inner_rect.size.x - inner_corner_radius[1], inner_rect.position.y + inner_corner_radius[1]), //tr
- inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2]), //br
- Point2(inner_rect.position.x + inner_corner_radius[3], inner_rect.position.y + inner_rect.size.y - inner_corner_radius[3]) //bl
- };
- // Calculate the vertices.
-
- // If the center is filled, we do not draw the border and directly use the inner ring as reference. Because all calls to this
- // method either draw a ring or a filled rounded rectangle, but not both.
- int max_inner_outer = draw_border ? 2 : 1;
-
- for (int corner_index = 0; corner_index < 4; corner_index++) {
- for (int detail = 0; detail <= adapted_corner_detail; detail++) {
- for (int inner_outer = 0; inner_outer < max_inner_outer; inner_outer++) {
- real_t radius;
- Color color;
- Point2 corner_point;
- if (inner_outer == 0) {
- radius = inner_corner_radius[corner_index];
- color = inner_color;
- corner_point = inner_points[corner_index];
- } else {
- radius = ring_corner_radius[corner_index];
- color = outer_color;
- corner_point = outer_points[corner_index];
- }
-
- const real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x;
- const real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y;
- const float x_skew = -skew.x * (y - ring_rect.get_center().y);
- const float y_skew = -skew.y * (x - ring_rect.get_center().x);
- verts.push_back(Vector2(x + x_skew, y + y_skew));
- colors.push_back(color);
- }
- }
- }
-
- int ring_vert_count = verts.size() - vert_offset;
-
- // Fill the indices and the colors for the border.
-
- if (draw_border) {
- for (int i = 0; i < ring_vert_count; i++) {
- indices.push_back(vert_offset + ((i + 0) % ring_vert_count));
- indices.push_back(vert_offset + ((i + 2) % ring_vert_count));
- indices.push_back(vert_offset + ((i + 1) % ring_vert_count));
- }
- }
-
- if (is_filled) {
- // Compute the triangles pattern to draw the rounded rectangle.
- // Consists of vertical stripes of two triangles each.
-
- int stripes_count = ring_vert_count / 2 - 1;
- int last_vert_id = ring_vert_count - 1;
-
- for (int i = 0; i < stripes_count; i++) {
- // Polygon 1.
- indices.push_back(vert_offset + i);
- indices.push_back(vert_offset + last_vert_id - i - 1);
- indices.push_back(vert_offset + i + 1);
- // Polygon 2.
- indices.push_back(vert_offset + i);
- indices.push_back(vert_offset + last_vert_id - 0 - i);
- indices.push_back(vert_offset + last_vert_id - 1 - i);
- }
- }
-}
-
-inline void adapt_values(int p_index_a, int p_index_b, real_t *adapted_values, const real_t *p_values, const real_t p_width, const real_t p_max_a, const real_t p_max_b) {
- if (p_values[p_index_a] + p_values[p_index_b] > p_width) {
- real_t factor;
- real_t new_value;
-
- factor = (real_t)p_width / (real_t)(p_values[p_index_a] + p_values[p_index_b]);
-
- new_value = (p_values[p_index_a] * factor);
- if (new_value < adapted_values[p_index_a]) {
- adapted_values[p_index_a] = new_value;
- }
- new_value = (p_values[p_index_b] * factor);
- if (new_value < adapted_values[p_index_b]) {
- adapted_values[p_index_b] = new_value;
- }
- } else {
- adapted_values[p_index_a] = MIN(p_values[p_index_a], adapted_values[p_index_a]);
- adapted_values[p_index_b] = MIN(p_values[p_index_b], adapted_values[p_index_b]);
- }
- adapted_values[p_index_a] = MIN(p_max_a, adapted_values[p_index_a]);
- adapted_values[p_index_b] = MIN(p_max_b, adapted_values[p_index_b]);
-}
-
-Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const {
- Rect2 draw_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]);
-
- if (shadow_size > 0) {
- Rect2 shadow_rect = draw_rect.grow(shadow_size);
- shadow_rect.position += shadow_offset;
- draw_rect = draw_rect.merge(shadow_rect);
- }
-
- return draw_rect;
-}
-
-void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
- bool draw_border = (border_width[0] > 0) || (border_width[1] > 0) || (border_width[2] > 0) || (border_width[3] > 0);
- bool draw_shadow = (shadow_size > 0);
- if (!draw_border && !draw_center && !draw_shadow) {
- return;
- }
-
- Rect2 style_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]);
- if (Math::is_zero_approx(style_rect.size.width) || Math::is_zero_approx(style_rect.size.height)) {
- return;
- }
-
- const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0);
- // Only enable antialiasing if it is actually needed. This improve performances
- // and maximizes sharpness for non-skewed StyleBoxes with sharp corners.
- const bool aa_on = (rounded_corners || !skew.is_zero_approx()) && anti_aliased;
-
- const bool blend_on = blend_border && draw_border;
-
- Color border_color_alpha = Color(border_color.r, border_color.g, border_color.b, 0);
- Color border_color_blend = (draw_center ? bg_color : border_color_alpha);
- Color border_color_inner = blend_on ? border_color_blend : border_color;
-
- // Adapt borders (prevent weird overlapping/glitchy drawings).
- real_t width = MAX(style_rect.size.width, 0);
- real_t height = MAX(style_rect.size.height, 0);
- real_t adapted_border[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 };
- adapt_values(SIDE_TOP, SIDE_BOTTOM, adapted_border, border_width, height, height, height);
- adapt_values(SIDE_LEFT, SIDE_RIGHT, adapted_border, border_width, width, width, width);
-
- // Adapt corners (prevent weird overlapping/glitchy drawings).
- real_t adapted_corner[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 };
- adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]);
- adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]);
- adapt_values(CORNER_TOP_LEFT, CORNER_TOP_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]);
- adapt_values(CORNER_BOTTOM_LEFT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]);
-
- Rect2 infill_rect = style_rect.grow_individual(-adapted_border[SIDE_LEFT], -adapted_border[SIDE_TOP], -adapted_border[SIDE_RIGHT], -adapted_border[SIDE_BOTTOM]);
-
- Rect2 border_style_rect = style_rect;
- if (aa_on) {
- for (int i = 0; i < 4; i++) {
- if (border_width[i] > 0) {
- border_style_rect = border_style_rect.grow_side((Side)i, -aa_size);
- }
- }
- }
-
- Vector<Point2> verts;
- Vector<int> indices;
- Vector<Color> colors;
- Vector<Point2> uvs;
-
- // Create shadow
- if (draw_shadow) {
- Rect2 shadow_inner_rect = style_rect;
- shadow_inner_rect.position += shadow_offset;
-
- Rect2 shadow_rect = style_rect.grow(shadow_size);
- shadow_rect.position += shadow_offset;
-
- Color shadow_color_transparent = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0);
-
- draw_rounded_rectangle(verts, indices, colors, shadow_inner_rect, adapted_corner,
- shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail, skew);
-
- if (draw_center) {
- draw_rounded_rectangle(verts, indices, colors, shadow_inner_rect, adapted_corner,
- shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, skew, true);
- }
- }
-
- // Create border (no AA).
- if (draw_border && !aa_on) {
- draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
- border_style_rect, infill_rect, border_color_inner, border_color, corner_detail, skew);
- }
-
- // Create infill (no AA).
- if (draw_center && (!aa_on || blend_on)) {
- draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
- infill_rect, infill_rect, bg_color, bg_color, corner_detail, skew, true);
- }
-
- if (aa_on) {
- real_t aa_border_width[4];
- real_t aa_border_width_half[4];
- real_t aa_fill_width[4];
- real_t aa_fill_width_half[4];
- if (draw_border) {
- for (int i = 0; i < 4; i++) {
- if (border_width[i] > 0) {
- aa_border_width[i] = aa_size;
- aa_border_width_half[i] = aa_size / 2;
- aa_fill_width[i] = 0;
- aa_fill_width_half[i] = 0;
- } else {
- aa_border_width[i] = 0;
- aa_border_width_half[i] = 0;
- aa_fill_width[i] = aa_size;
- aa_fill_width_half[i] = aa_size / 2;
- }
- }
- } else {
- for (int i = 0; i < 4; i++) {
- aa_border_width[i] = 0;
- aa_border_width_half[i] = 0;
- aa_fill_width[i] = aa_size;
- aa_fill_width_half[i] = aa_size / 2;
- }
- }
-
- if (draw_center) {
- // Infill rect, transparent side of antialiasing gradient (base infill rect enlarged by AA size)
- Rect2 infill_rect_aa_transparent = infill_rect.grow_individual(aa_fill_width_half[SIDE_LEFT], aa_fill_width_half[SIDE_TOP],
- aa_fill_width_half[SIDE_RIGHT], aa_fill_width_half[SIDE_BOTTOM]);
- // Infill rect, colored side of antialiasing gradient (base infill rect shrunk by AA size)
- Rect2 infill_rect_aa_colored = infill_rect_aa_transparent.grow_individual(-aa_fill_width[SIDE_LEFT], -aa_fill_width[SIDE_TOP],
- -aa_fill_width[SIDE_RIGHT], -aa_fill_width[SIDE_BOTTOM]);
- if (!blend_on) {
- // Create center fill, not antialiased yet
- draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
- infill_rect_aa_colored, infill_rect_aa_colored, bg_color, bg_color, corner_detail, skew, true);
- }
- if (!blend_on || !draw_border) {
- Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0);
- // Add antialiasing on the center fill
- draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
- infill_rect_aa_transparent, infill_rect_aa_colored, bg_color, alpha_bg, corner_detail, skew);
- }
- }
-
- if (draw_border) {
- // Inner border recct, fully colored side of antialiasing gradient (base inner rect enlarged by AA size)
- Rect2 inner_rect_aa_colored = infill_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP],
- aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]);
- // Inner border rect, transparent side of antialiasing gradient (base inner rect shrunk by AA size)
- Rect2 inner_rect_aa_transparent = inner_rect_aa_colored.grow_individual(-aa_border_width[SIDE_LEFT], -aa_border_width[SIDE_TOP],
- -aa_border_width[SIDE_RIGHT], -aa_border_width[SIDE_BOTTOM]);
- // Outer border rect, transparent side of antialiasing gradient (base outer rect enlarged by AA size)
- Rect2 outer_rect_aa_transparent = style_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP],
- aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]);
- // Outer border rect, colored side of antialiasing gradient (base outer rect shrunk by AA size)
- Rect2 outer_rect_aa_colored = border_style_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP],
- aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]);
-
- // Create border ring, not antialiased yet
- draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
- outer_rect_aa_colored, ((blend_on) ? infill_rect : inner_rect_aa_colored), border_color_inner, border_color, corner_detail, skew);
- if (!blend_on) {
- // Add antialiasing on the ring inner border
- draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
- inner_rect_aa_colored, inner_rect_aa_transparent, border_color_blend, border_color, corner_detail, skew);
- }
- // Add antialiasing on the ring outer border
- draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
- outer_rect_aa_transparent, outer_rect_aa_colored, border_color, border_color_alpha, corner_detail, skew);
- }
- }
-
- // Compute UV coordinates.
- Rect2 uv_rect = style_rect.grow(aa_on ? aa_size : 0);
- uvs.resize(verts.size());
- for (int i = 0; i < verts.size(); i++) {
- uvs.write[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width;
- uvs.write[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height;
- }
-
- // Draw stylebox.
- RenderingServer *vs = RenderingServer::get_singleton();
- vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors, uvs);
-}
-
-float StyleBoxFlat::get_style_margin(Side p_side) const {
- ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
- return border_width[p_side];
-}
-
-void StyleBoxFlat::_validate_property(PropertyInfo &p_property) const {
- if (!anti_aliased && p_property.name == "anti_aliasing_size") {
- p_property.usage = PROPERTY_USAGE_NO_EDITOR;
- }
-}
-
-void StyleBoxFlat::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_bg_color", "color"), &StyleBoxFlat::set_bg_color);
- ClassDB::bind_method(D_METHOD("get_bg_color"), &StyleBoxFlat::get_bg_color);
-
- ClassDB::bind_method(D_METHOD("set_border_color", "color"), &StyleBoxFlat::set_border_color);
- ClassDB::bind_method(D_METHOD("get_border_color"), &StyleBoxFlat::get_border_color);
-
- ClassDB::bind_method(D_METHOD("set_border_width_all", "width"), &StyleBoxFlat::set_border_width_all);
- ClassDB::bind_method(D_METHOD("get_border_width_min"), &StyleBoxFlat::get_border_width_min);
-
- ClassDB::bind_method(D_METHOD("set_border_width", "margin", "width"), &StyleBoxFlat::set_border_width);
- ClassDB::bind_method(D_METHOD("get_border_width", "margin"), &StyleBoxFlat::get_border_width);
-
- ClassDB::bind_method(D_METHOD("set_border_blend", "blend"), &StyleBoxFlat::set_border_blend);
- ClassDB::bind_method(D_METHOD("get_border_blend"), &StyleBoxFlat::get_border_blend);
-
- ClassDB::bind_method(D_METHOD("set_corner_radius_all", "radius"), &StyleBoxFlat::set_corner_radius_all);
-
- ClassDB::bind_method(D_METHOD("set_corner_radius", "corner", "radius"), &StyleBoxFlat::set_corner_radius);
- ClassDB::bind_method(D_METHOD("get_corner_radius", "corner"), &StyleBoxFlat::get_corner_radius);
-
- ClassDB::bind_method(D_METHOD("set_expand_margin", "margin", "size"), &StyleBoxFlat::set_expand_margin);
- ClassDB::bind_method(D_METHOD("set_expand_margin_all", "size"), &StyleBoxFlat::set_expand_margin_all);
- ClassDB::bind_method(D_METHOD("get_expand_margin", "margin"), &StyleBoxFlat::get_expand_margin);
-
- ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &StyleBoxFlat::set_draw_center);
- ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxFlat::is_draw_center_enabled);
-
- ClassDB::bind_method(D_METHOD("set_skew", "skew"), &StyleBoxFlat::set_skew);
- ClassDB::bind_method(D_METHOD("get_skew"), &StyleBoxFlat::get_skew);
-
- ClassDB::bind_method(D_METHOD("set_shadow_color", "color"), &StyleBoxFlat::set_shadow_color);
- ClassDB::bind_method(D_METHOD("get_shadow_color"), &StyleBoxFlat::get_shadow_color);
-
- ClassDB::bind_method(D_METHOD("set_shadow_size", "size"), &StyleBoxFlat::set_shadow_size);
- ClassDB::bind_method(D_METHOD("get_shadow_size"), &StyleBoxFlat::get_shadow_size);
-
- ClassDB::bind_method(D_METHOD("set_shadow_offset", "offset"), &StyleBoxFlat::set_shadow_offset);
- ClassDB::bind_method(D_METHOD("get_shadow_offset"), &StyleBoxFlat::get_shadow_offset);
-
- ClassDB::bind_method(D_METHOD("set_anti_aliased", "anti_aliased"), &StyleBoxFlat::set_anti_aliased);
- ClassDB::bind_method(D_METHOD("is_anti_aliased"), &StyleBoxFlat::is_anti_aliased);
-
- ClassDB::bind_method(D_METHOD("set_aa_size", "size"), &StyleBoxFlat::set_aa_size);
- ClassDB::bind_method(D_METHOD("get_aa_size"), &StyleBoxFlat::get_aa_size);
-
- ClassDB::bind_method(D_METHOD("set_corner_detail", "detail"), &StyleBoxFlat::set_corner_detail);
- ClassDB::bind_method(D_METHOD("get_corner_detail"), &StyleBoxFlat::get_corner_detail);
-
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "bg_color"), "set_bg_color", "get_bg_color");
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "skew"), "set_skew", "get_skew");
-
- ADD_GROUP("Border Width", "border_width_");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_BOTTOM);
-
- ADD_GROUP("Border", "border_");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color");
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "border_blend"), "set_border_blend", "get_border_blend");
-
- ADD_GROUP("Corner Radius", "corner_radius_");
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_LEFT);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "corner_detail", PROPERTY_HINT_RANGE, "1,20,1"), "set_corner_detail", "get_corner_detail");
-
- ADD_GROUP("Expand Margins", "expand_margin_");
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM);
-
- ADD_GROUP("Shadow", "shadow_");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset");
-
- ADD_GROUP("Anti Aliasing", "anti_aliasing_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "anti_aliasing"), "set_anti_aliased", "is_anti_aliased");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anti_aliasing_size", PROPERTY_HINT_RANGE, "0.01,10,0.001,suffix:px"), "set_aa_size", "get_aa_size");
-}
-
-StyleBoxFlat::StyleBoxFlat() {}
-
-StyleBoxFlat::~StyleBoxFlat() {}
-
-void StyleBoxLine::set_color(const Color &p_color) {
- color = p_color;
- emit_changed();
-}
-
-Color StyleBoxLine::get_color() const {
- return color;
-}
-
-void StyleBoxLine::set_thickness(int p_thickness) {
- thickness = p_thickness;
- emit_changed();
-}
-
-int StyleBoxLine::get_thickness() const {
- return thickness;
-}
-
-void StyleBoxLine::set_vertical(bool p_vertical) {
- vertical = p_vertical;
- emit_changed();
-}
-
-bool StyleBoxLine::is_vertical() const {
- return vertical;
-}
-
-void StyleBoxLine::set_grow_end(float p_grow_end) {
- grow_end = p_grow_end;
- emit_changed();
-}
-
-float StyleBoxLine::get_grow_end() const {
- return grow_end;
-}
-
-void StyleBoxLine::set_grow_begin(float p_grow_begin) {
- grow_begin = p_grow_begin;
- emit_changed();
-}
-
-float StyleBoxLine::get_grow_begin() const {
- return grow_begin;
-}
-
-void StyleBoxLine::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_color", "color"), &StyleBoxLine::set_color);
- ClassDB::bind_method(D_METHOD("get_color"), &StyleBoxLine::get_color);
- ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &StyleBoxLine::set_thickness);
- ClassDB::bind_method(D_METHOD("get_thickness"), &StyleBoxLine::get_thickness);
- ClassDB::bind_method(D_METHOD("set_grow_begin", "offset"), &StyleBoxLine::set_grow_begin);
- ClassDB::bind_method(D_METHOD("get_grow_begin"), &StyleBoxLine::get_grow_begin);
- ClassDB::bind_method(D_METHOD("set_grow_end", "offset"), &StyleBoxLine::set_grow_end);
- ClassDB::bind_method(D_METHOD("get_grow_end"), &StyleBoxLine::get_grow_end);
- ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &StyleBoxLine::set_vertical);
- ClassDB::bind_method(D_METHOD("is_vertical"), &StyleBoxLine::is_vertical);
-
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_begin", "get_grow_begin");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_end", "get_grow_end");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,100,suffix:px"), "set_thickness", "get_thickness");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
-}
-
-float StyleBoxLine::get_style_margin(Side p_side) const {
- ERR_FAIL_INDEX_V((int)p_side, 4, 0);
-
- if (vertical) {
- if (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) {
- return thickness / 2.0;
- }
- } else if (p_side == SIDE_TOP || p_side == SIDE_BOTTOM) {
- return thickness / 2.0;
- }
-
- return 0;
-}
-
-void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const {
- RenderingServer *vs = RenderingServer::get_singleton();
- Rect2i r = p_rect;
-
- if (vertical) {
- r.position.y -= grow_begin;
- r.size.y += (grow_begin + grow_end);
- r.size.x = thickness;
- } else {
- r.position.x -= grow_begin;
- r.size.x += (grow_begin + grow_end);
- r.size.y = thickness;
- }
-
- vs->canvas_item_add_rect(p_canvas_item, r, color);
-}
-
-StyleBoxLine::StyleBoxLine() {}
-
-StyleBoxLine::~StyleBoxLine() {}
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index 9d96e9a3b7..3f9a96be2f 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -32,8 +32,9 @@
#define STYLE_BOX_H
#include "core/io/resource.h"
-#include "scene/resources/texture.h"
-#include "servers/rendering_server.h"
+#include "core/object/class_db.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
class CanvasItem;
@@ -41,11 +42,12 @@ class StyleBox : public Resource {
GDCLASS(StyleBox, Resource);
RES_BASE_EXTENSION("stylebox");
OBJ_SAVE_TYPE(StyleBox);
+
float content_margin[4];
protected:
- virtual float get_style_margin(Side p_side) const { return 0; }
static void _bind_methods();
+ virtual float get_style_margin(Side p_side) const { return 0; }
GDVIRTUAL2C(_draw, RID, Rect2)
GDVIRTUAL1RC(Rect2, _get_draw_rect, Rect2)
@@ -82,184 +84,4 @@ public:
StyleBoxEmpty() {}
};
-class StyleBoxTexture : public StyleBox {
- GDCLASS(StyleBoxTexture, StyleBox);
-
-public:
- enum AxisStretchMode {
- AXIS_STRETCH_MODE_STRETCH,
- AXIS_STRETCH_MODE_TILE,
- AXIS_STRETCH_MODE_TILE_FIT,
- };
-
-private:
- float expand_margin[4] = {};
- float texture_margin[4] = {};
- Rect2 region_rect;
- Ref<Texture2D> texture;
- bool draw_center = true;
- Color modulate = Color(1, 1, 1, 1);
- AxisStretchMode axis_h = AXIS_STRETCH_MODE_STRETCH;
- AxisStretchMode axis_v = AXIS_STRETCH_MODE_STRETCH;
-
-protected:
- virtual float get_style_margin(Side p_side) const override;
- static void _bind_methods();
-
-public:
- void set_expand_margin(Side p_expand_side, float p_size);
- void set_expand_margin_all(float p_expand_margin_size);
- void set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom);
- float get_expand_margin(Side p_expand_side) const;
-
- void set_texture_margin(Side p_side, float p_size);
- void set_texture_margin_all(float p_size);
- void set_texture_margin_individual(float p_left, float p_top, float p_right, float p_bottom);
- float get_texture_margin(Side p_side) const;
-
- void set_region_rect(const Rect2 &p_region_rect);
- Rect2 get_region_rect() const;
-
- void set_texture(Ref<Texture2D> p_texture);
- Ref<Texture2D> get_texture() const;
-
- void set_draw_center(bool p_enabled);
- bool is_draw_center_enabled() const;
-
- void set_h_axis_stretch_mode(AxisStretchMode p_mode);
- AxisStretchMode get_h_axis_stretch_mode() const;
-
- void set_v_axis_stretch_mode(AxisStretchMode p_mode);
- AxisStretchMode get_v_axis_stretch_mode() const;
-
- void set_modulate(const Color &p_modulate);
- Color get_modulate() const;
-
- virtual Rect2 get_draw_rect(const Rect2 &p_rect) const override;
- virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override;
-
- StyleBoxTexture();
- ~StyleBoxTexture();
-};
-
-VARIANT_ENUM_CAST(StyleBoxTexture::AxisStretchMode)
-
-class StyleBoxFlat : public StyleBox {
- GDCLASS(StyleBoxFlat, StyleBox);
-
- Color bg_color = Color(0.6, 0.6, 0.6);
- Color shadow_color = Color(0, 0, 0, 0.6);
- Color border_color = Color(0.8, 0.8, 0.8);
-
- real_t border_width[4] = {};
- real_t expand_margin[4] = {};
- real_t corner_radius[4] = {};
-
- bool draw_center = true;
- bool blend_border = false;
- Vector2 skew;
- bool anti_aliased = true;
-
- int corner_detail = 8;
- int shadow_size = 0;
- Point2 shadow_offset;
- real_t aa_size = 1;
-
-protected:
- virtual float get_style_margin(Side p_side) const override;
- static void _bind_methods();
- void _validate_property(PropertyInfo &p_property) const;
-
-public:
- void set_bg_color(const Color &p_color);
- Color get_bg_color() const;
-
- void set_border_color(const Color &p_color);
- Color get_border_color() const;
-
- void set_border_width_all(int p_size);
- int get_border_width_min() const;
-
- void set_border_width(Side p_side, int p_width);
- int get_border_width(Side p_side) const;
-
- void set_border_blend(bool p_blend);
- bool get_border_blend() const;
-
- void set_corner_radius_all(int radius);
- void set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left);
-
- void set_corner_radius(Corner p_corner, const int radius);
- int get_corner_radius(Corner p_corner) const;
-
- void set_corner_detail(const int &p_corner_detail);
- int get_corner_detail() const;
-
- void set_expand_margin(Side p_expand_side, float p_size);
- void set_expand_margin_all(float p_expand_margin_size);
- void set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom);
- float get_expand_margin(Side p_expand_side) const;
-
- void set_draw_center(bool p_enabled);
- bool is_draw_center_enabled() const;
-
- void set_skew(Vector2 p_skew);
- Vector2 get_skew() const;
-
- void set_shadow_color(const Color &p_color);
- Color get_shadow_color() const;
-
- void set_shadow_size(const int &p_size);
- int get_shadow_size() const;
-
- void set_shadow_offset(const Point2 &p_offset);
- Point2 get_shadow_offset() const;
-
- void set_anti_aliased(const bool &p_anti_aliased);
- bool is_anti_aliased() const;
- void set_aa_size(const real_t p_aa_size);
- real_t get_aa_size() const;
-
- virtual Rect2 get_draw_rect(const Rect2 &p_rect) const override;
- virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override;
-
- StyleBoxFlat();
- ~StyleBoxFlat();
-};
-
-// Just used to draw lines.
-class StyleBoxLine : public StyleBox {
- GDCLASS(StyleBoxLine, StyleBox);
- Color color;
- int thickness = 1;
- bool vertical = false;
- float grow_begin = 1.0;
- float grow_end = 1.0;
-
-protected:
- virtual float get_style_margin(Side p_side) const override;
- static void _bind_methods();
-
-public:
- void set_color(const Color &p_color);
- Color get_color() const;
-
- void set_thickness(int p_thickness);
- int get_thickness() const;
-
- void set_vertical(bool p_vertical);
- bool is_vertical() const;
-
- void set_grow_begin(float p_grow);
- float get_grow_begin() const;
-
- void set_grow_end(float p_grow);
- float get_grow_end() const;
-
- virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override;
-
- StyleBoxLine();
- ~StyleBoxLine();
-};
-
#endif // STYLE_BOX_H
diff --git a/scene/resources/style_box_flat.cpp b/scene/resources/style_box_flat.cpp
new file mode 100644
index 0000000000..52d02e92cb
--- /dev/null
+++ b/scene/resources/style_box_flat.cpp
@@ -0,0 +1,642 @@
+/**************************************************************************/
+/* style_box_flat.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 "style_box_flat.h"
+
+#include "servers/rendering_server.h"
+
+float StyleBoxFlat::get_style_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
+ return border_width[p_side];
+}
+
+void StyleBoxFlat::_validate_property(PropertyInfo &p_property) const {
+ if (!anti_aliased && p_property.name == "anti_aliasing_size") {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+}
+
+void StyleBoxFlat::set_bg_color(const Color &p_color) {
+ bg_color = p_color;
+ emit_changed();
+}
+
+Color StyleBoxFlat::get_bg_color() const {
+ return bg_color;
+}
+
+void StyleBoxFlat::set_border_color(const Color &p_color) {
+ border_color = p_color;
+ emit_changed();
+}
+
+Color StyleBoxFlat::get_border_color() const {
+ return border_color;
+}
+
+void StyleBoxFlat::set_border_width_all(int p_size) {
+ border_width[0] = p_size;
+ border_width[1] = p_size;
+ border_width[2] = p_size;
+ border_width[3] = p_size;
+ emit_changed();
+}
+
+int StyleBoxFlat::get_border_width_min() const {
+ return MIN(MIN(border_width[0], border_width[1]), MIN(border_width[2], border_width[3]));
+}
+
+void StyleBoxFlat::set_border_width(Side p_side, int p_width) {
+ ERR_FAIL_INDEX((int)p_side, 4);
+ border_width[p_side] = p_width;
+ emit_changed();
+}
+
+int StyleBoxFlat::get_border_width(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0);
+ return border_width[p_side];
+}
+
+void StyleBoxFlat::set_border_blend(bool p_blend) {
+ blend_border = p_blend;
+ emit_changed();
+}
+
+bool StyleBoxFlat::get_border_blend() const {
+ return blend_border;
+}
+
+void StyleBoxFlat::set_corner_radius(const Corner p_corner, const int radius) {
+ ERR_FAIL_INDEX((int)p_corner, 4);
+ corner_radius[p_corner] = radius;
+ emit_changed();
+}
+
+void StyleBoxFlat::set_corner_radius_all(int radius) {
+ for (int i = 0; i < 4; i++) {
+ corner_radius[i] = radius;
+ }
+
+ emit_changed();
+}
+
+void StyleBoxFlat::set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left) {
+ corner_radius[0] = radius_top_left;
+ corner_radius[1] = radius_top_right;
+ corner_radius[2] = radius_bottom_right;
+ corner_radius[3] = radius_bottom_left;
+
+ emit_changed();
+}
+
+int StyleBoxFlat::get_corner_radius(const Corner p_corner) const {
+ ERR_FAIL_INDEX_V((int)p_corner, 4, 0);
+ return corner_radius[p_corner];
+}
+
+void StyleBoxFlat::set_corner_detail(const int &p_corner_detail) {
+ corner_detail = CLAMP(p_corner_detail, 1, 20);
+ emit_changed();
+}
+
+int StyleBoxFlat::get_corner_detail() const {
+ return corner_detail;
+}
+
+void StyleBoxFlat::set_expand_margin(Side p_side, float p_size) {
+ ERR_FAIL_INDEX((int)p_side, 4);
+ expand_margin[p_side] = p_size;
+ emit_changed();
+}
+
+void StyleBoxFlat::set_expand_margin_all(float p_expand_margin_size) {
+ for (int i = 0; i < 4; i++) {
+ expand_margin[i] = p_expand_margin_size;
+ }
+ emit_changed();
+}
+
+void StyleBoxFlat::set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom) {
+ expand_margin[SIDE_LEFT] = p_left;
+ expand_margin[SIDE_TOP] = p_top;
+ expand_margin[SIDE_RIGHT] = p_right;
+ expand_margin[SIDE_BOTTOM] = p_bottom;
+ emit_changed();
+}
+
+float StyleBoxFlat::get_expand_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
+ return expand_margin[p_side];
+}
+
+void StyleBoxFlat::set_draw_center(bool p_enabled) {
+ draw_center = p_enabled;
+ emit_changed();
+}
+
+bool StyleBoxFlat::is_draw_center_enabled() const {
+ return draw_center;
+}
+
+void StyleBoxFlat::set_skew(Vector2 p_skew) {
+ skew = p_skew;
+ emit_changed();
+}
+
+Vector2 StyleBoxFlat::get_skew() const {
+ return skew;
+}
+
+void StyleBoxFlat::set_shadow_color(const Color &p_color) {
+ shadow_color = p_color;
+ emit_changed();
+}
+
+Color StyleBoxFlat::get_shadow_color() const {
+ return shadow_color;
+}
+
+void StyleBoxFlat::set_shadow_size(const int &p_size) {
+ shadow_size = p_size;
+ emit_changed();
+}
+
+int StyleBoxFlat::get_shadow_size() const {
+ return shadow_size;
+}
+
+void StyleBoxFlat::set_shadow_offset(const Point2 &p_offset) {
+ shadow_offset = p_offset;
+ emit_changed();
+}
+
+Point2 StyleBoxFlat::get_shadow_offset() const {
+ return shadow_offset;
+}
+
+void StyleBoxFlat::set_anti_aliased(const bool &p_anti_aliased) {
+ anti_aliased = p_anti_aliased;
+ emit_changed();
+ notify_property_list_changed();
+}
+
+bool StyleBoxFlat::is_anti_aliased() const {
+ return anti_aliased;
+}
+
+void StyleBoxFlat::set_aa_size(const real_t p_aa_size) {
+ aa_size = CLAMP(p_aa_size, 0.01, 10);
+ emit_changed();
+}
+
+real_t StyleBoxFlat::get_aa_size() const {
+ return aa_size;
+}
+
+inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_rect, const real_t corner_radius[4], real_t *inner_corner_radius) {
+ real_t border_left = inner_rect.position.x - style_rect.position.x;
+ real_t border_top = inner_rect.position.y - style_rect.position.y;
+ real_t border_right = style_rect.size.width - inner_rect.size.width - border_left;
+ real_t border_bottom = style_rect.size.height - inner_rect.size.height - border_top;
+
+ real_t rad;
+
+ // Top left.
+ rad = MIN(border_top, border_left);
+ inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0);
+
+ // Top right;
+ rad = MIN(border_top, border_right);
+ inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0);
+
+ // Bottom right.
+ rad = MIN(border_bottom, border_right);
+ inner_corner_radius[2] = MAX(corner_radius[2] - rad, 0);
+
+ // Bottom left.
+ rad = MIN(border_bottom, border_left);
+ inner_corner_radius[3] = MAX(corner_radius[3] - rad, 0);
+}
+
+inline void draw_rounded_rectangle(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4],
+ const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const Vector2 &skew, bool is_filled = false) {
+ int vert_offset = verts.size();
+ if (!vert_offset) {
+ vert_offset = 0;
+ }
+
+ int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail;
+
+ bool draw_border = !is_filled;
+
+ real_t ring_corner_radius[4];
+ set_inner_corner_radius(style_rect, ring_rect, corner_radius, ring_corner_radius);
+
+ // Corner radius center points.
+ Vector<Point2> outer_points = {
+ ring_rect.position + Vector2(ring_corner_radius[0], ring_corner_radius[0]), //tl
+ Point2(ring_rect.position.x + ring_rect.size.x - ring_corner_radius[1], ring_rect.position.y + ring_corner_radius[1]), //tr
+ ring_rect.position + ring_rect.size - Vector2(ring_corner_radius[2], ring_corner_radius[2]), //br
+ Point2(ring_rect.position.x + ring_corner_radius[3], ring_rect.position.y + ring_rect.size.y - ring_corner_radius[3]) //bl
+ };
+
+ real_t inner_corner_radius[4];
+ set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius);
+
+ Vector<Point2> inner_points = {
+ inner_rect.position + Vector2(inner_corner_radius[0], inner_corner_radius[0]), //tl
+ Point2(inner_rect.position.x + inner_rect.size.x - inner_corner_radius[1], inner_rect.position.y + inner_corner_radius[1]), //tr
+ inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2]), //br
+ Point2(inner_rect.position.x + inner_corner_radius[3], inner_rect.position.y + inner_rect.size.y - inner_corner_radius[3]) //bl
+ };
+ // Calculate the vertices.
+
+ // If the center is filled, we do not draw the border and directly use the inner ring as reference. Because all calls to this
+ // method either draw a ring or a filled rounded rectangle, but not both.
+ int max_inner_outer = draw_border ? 2 : 1;
+
+ for (int corner_index = 0; corner_index < 4; corner_index++) {
+ for (int detail = 0; detail <= adapted_corner_detail; detail++) {
+ for (int inner_outer = 0; inner_outer < max_inner_outer; inner_outer++) {
+ real_t radius;
+ Color color;
+ Point2 corner_point;
+ if (inner_outer == 0) {
+ radius = inner_corner_radius[corner_index];
+ color = inner_color;
+ corner_point = inner_points[corner_index];
+ } else {
+ radius = ring_corner_radius[corner_index];
+ color = outer_color;
+ corner_point = outer_points[corner_index];
+ }
+
+ const real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x;
+ const real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y;
+ const float x_skew = -skew.x * (y - ring_rect.get_center().y);
+ const float y_skew = -skew.y * (x - ring_rect.get_center().x);
+ verts.push_back(Vector2(x + x_skew, y + y_skew));
+ colors.push_back(color);
+ }
+ }
+ }
+
+ int ring_vert_count = verts.size() - vert_offset;
+
+ // Fill the indices and the colors for the border.
+
+ if (draw_border) {
+ for (int i = 0; i < ring_vert_count; i++) {
+ indices.push_back(vert_offset + ((i + 0) % ring_vert_count));
+ indices.push_back(vert_offset + ((i + 2) % ring_vert_count));
+ indices.push_back(vert_offset + ((i + 1) % ring_vert_count));
+ }
+ }
+
+ if (is_filled) {
+ // Compute the triangles pattern to draw the rounded rectangle.
+ // Consists of vertical stripes of two triangles each.
+
+ int stripes_count = ring_vert_count / 2 - 1;
+ int last_vert_id = ring_vert_count - 1;
+
+ for (int i = 0; i < stripes_count; i++) {
+ // Polygon 1.
+ indices.push_back(vert_offset + i);
+ indices.push_back(vert_offset + last_vert_id - i - 1);
+ indices.push_back(vert_offset + i + 1);
+ // Polygon 2.
+ indices.push_back(vert_offset + i);
+ indices.push_back(vert_offset + last_vert_id - 0 - i);
+ indices.push_back(vert_offset + last_vert_id - 1 - i);
+ }
+ }
+}
+
+inline void adapt_values(int p_index_a, int p_index_b, real_t *adapted_values, const real_t *p_values, const real_t p_width, const real_t p_max_a, const real_t p_max_b) {
+ if (p_values[p_index_a] + p_values[p_index_b] > p_width) {
+ real_t factor;
+ real_t new_value;
+
+ factor = (real_t)p_width / (real_t)(p_values[p_index_a] + p_values[p_index_b]);
+
+ new_value = (p_values[p_index_a] * factor);
+ if (new_value < adapted_values[p_index_a]) {
+ adapted_values[p_index_a] = new_value;
+ }
+ new_value = (p_values[p_index_b] * factor);
+ if (new_value < adapted_values[p_index_b]) {
+ adapted_values[p_index_b] = new_value;
+ }
+ } else {
+ adapted_values[p_index_a] = MIN(p_values[p_index_a], adapted_values[p_index_a]);
+ adapted_values[p_index_b] = MIN(p_values[p_index_b], adapted_values[p_index_b]);
+ }
+ adapted_values[p_index_a] = MIN(p_max_a, adapted_values[p_index_a]);
+ adapted_values[p_index_b] = MIN(p_max_b, adapted_values[p_index_b]);
+}
+
+Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const {
+ Rect2 draw_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]);
+
+ if (shadow_size > 0) {
+ Rect2 shadow_rect = draw_rect.grow(shadow_size);
+ shadow_rect.position += shadow_offset;
+ draw_rect = draw_rect.merge(shadow_rect);
+ }
+
+ return draw_rect;
+}
+
+void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
+ bool draw_border = (border_width[0] > 0) || (border_width[1] > 0) || (border_width[2] > 0) || (border_width[3] > 0);
+ bool draw_shadow = (shadow_size > 0);
+ if (!draw_border && !draw_center && !draw_shadow) {
+ return;
+ }
+
+ Rect2 style_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]);
+ if (Math::is_zero_approx(style_rect.size.width) || Math::is_zero_approx(style_rect.size.height)) {
+ return;
+ }
+
+ const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0);
+ // Only enable antialiasing if it is actually needed. This improve performances
+ // and maximizes sharpness for non-skewed StyleBoxes with sharp corners.
+ const bool aa_on = (rounded_corners || !skew.is_zero_approx()) && anti_aliased;
+
+ const bool blend_on = blend_border && draw_border;
+
+ Color border_color_alpha = Color(border_color.r, border_color.g, border_color.b, 0);
+ Color border_color_blend = (draw_center ? bg_color : border_color_alpha);
+ Color border_color_inner = blend_on ? border_color_blend : border_color;
+
+ // Adapt borders (prevent weird overlapping/glitchy drawings).
+ real_t width = MAX(style_rect.size.width, 0);
+ real_t height = MAX(style_rect.size.height, 0);
+ real_t adapted_border[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 };
+ adapt_values(SIDE_TOP, SIDE_BOTTOM, adapted_border, border_width, height, height, height);
+ adapt_values(SIDE_LEFT, SIDE_RIGHT, adapted_border, border_width, width, width, width);
+
+ // Adapt corners (prevent weird overlapping/glitchy drawings).
+ real_t adapted_corner[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 };
+ adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]);
+ adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]);
+ adapt_values(CORNER_TOP_LEFT, CORNER_TOP_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]);
+ adapt_values(CORNER_BOTTOM_LEFT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]);
+
+ Rect2 infill_rect = style_rect.grow_individual(-adapted_border[SIDE_LEFT], -adapted_border[SIDE_TOP], -adapted_border[SIDE_RIGHT], -adapted_border[SIDE_BOTTOM]);
+
+ Rect2 border_style_rect = style_rect;
+ if (aa_on) {
+ for (int i = 0; i < 4; i++) {
+ if (border_width[i] > 0) {
+ border_style_rect = border_style_rect.grow_side((Side)i, -aa_size);
+ }
+ }
+ }
+
+ Vector<Point2> verts;
+ Vector<int> indices;
+ Vector<Color> colors;
+ Vector<Point2> uvs;
+
+ // Create shadow
+ if (draw_shadow) {
+ Rect2 shadow_inner_rect = style_rect;
+ shadow_inner_rect.position += shadow_offset;
+
+ Rect2 shadow_rect = style_rect.grow(shadow_size);
+ shadow_rect.position += shadow_offset;
+
+ Color shadow_color_transparent = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0);
+
+ draw_rounded_rectangle(verts, indices, colors, shadow_inner_rect, adapted_corner,
+ shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail, skew);
+
+ if (draw_center) {
+ draw_rounded_rectangle(verts, indices, colors, shadow_inner_rect, adapted_corner,
+ shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, skew, true);
+ }
+ }
+
+ // Create border (no AA).
+ if (draw_border && !aa_on) {
+ draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
+ border_style_rect, infill_rect, border_color_inner, border_color, corner_detail, skew);
+ }
+
+ // Create infill (no AA).
+ if (draw_center && (!aa_on || blend_on)) {
+ draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
+ infill_rect, infill_rect, bg_color, bg_color, corner_detail, skew, true);
+ }
+
+ if (aa_on) {
+ real_t aa_border_width[4];
+ real_t aa_border_width_half[4];
+ real_t aa_fill_width[4];
+ real_t aa_fill_width_half[4];
+ if (draw_border) {
+ for (int i = 0; i < 4; i++) {
+ if (border_width[i] > 0) {
+ aa_border_width[i] = aa_size;
+ aa_border_width_half[i] = aa_size / 2;
+ aa_fill_width[i] = 0;
+ aa_fill_width_half[i] = 0;
+ } else {
+ aa_border_width[i] = 0;
+ aa_border_width_half[i] = 0;
+ aa_fill_width[i] = aa_size;
+ aa_fill_width_half[i] = aa_size / 2;
+ }
+ }
+ } else {
+ for (int i = 0; i < 4; i++) {
+ aa_border_width[i] = 0;
+ aa_border_width_half[i] = 0;
+ aa_fill_width[i] = aa_size;
+ aa_fill_width_half[i] = aa_size / 2;
+ }
+ }
+
+ if (draw_center) {
+ // Infill rect, transparent side of antialiasing gradient (base infill rect enlarged by AA size)
+ Rect2 infill_rect_aa_transparent = infill_rect.grow_individual(aa_fill_width_half[SIDE_LEFT], aa_fill_width_half[SIDE_TOP],
+ aa_fill_width_half[SIDE_RIGHT], aa_fill_width_half[SIDE_BOTTOM]);
+ // Infill rect, colored side of antialiasing gradient (base infill rect shrunk by AA size)
+ Rect2 infill_rect_aa_colored = infill_rect_aa_transparent.grow_individual(-aa_fill_width[SIDE_LEFT], -aa_fill_width[SIDE_TOP],
+ -aa_fill_width[SIDE_RIGHT], -aa_fill_width[SIDE_BOTTOM]);
+ if (!blend_on) {
+ // Create center fill, not antialiased yet
+ draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
+ infill_rect_aa_colored, infill_rect_aa_colored, bg_color, bg_color, corner_detail, skew, true);
+ }
+ if (!blend_on || !draw_border) {
+ Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0);
+ // Add antialiasing on the center fill
+ draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
+ infill_rect_aa_transparent, infill_rect_aa_colored, bg_color, alpha_bg, corner_detail, skew);
+ }
+ }
+
+ if (draw_border) {
+ // Inner border recct, fully colored side of antialiasing gradient (base inner rect enlarged by AA size)
+ Rect2 inner_rect_aa_colored = infill_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP],
+ aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]);
+ // Inner border rect, transparent side of antialiasing gradient (base inner rect shrunk by AA size)
+ Rect2 inner_rect_aa_transparent = inner_rect_aa_colored.grow_individual(-aa_border_width[SIDE_LEFT], -aa_border_width[SIDE_TOP],
+ -aa_border_width[SIDE_RIGHT], -aa_border_width[SIDE_BOTTOM]);
+ // Outer border rect, transparent side of antialiasing gradient (base outer rect enlarged by AA size)
+ Rect2 outer_rect_aa_transparent = style_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP],
+ aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]);
+ // Outer border rect, colored side of antialiasing gradient (base outer rect shrunk by AA size)
+ Rect2 outer_rect_aa_colored = border_style_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP],
+ aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]);
+
+ // Create border ring, not antialiased yet
+ draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
+ outer_rect_aa_colored, ((blend_on) ? infill_rect : inner_rect_aa_colored), border_color_inner, border_color, corner_detail, skew);
+ if (!blend_on) {
+ // Add antialiasing on the ring inner border
+ draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
+ inner_rect_aa_colored, inner_rect_aa_transparent, border_color_blend, border_color, corner_detail, skew);
+ }
+ // Add antialiasing on the ring outer border
+ draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner,
+ outer_rect_aa_transparent, outer_rect_aa_colored, border_color, border_color_alpha, corner_detail, skew);
+ }
+ }
+
+ // Compute UV coordinates.
+ Rect2 uv_rect = style_rect.grow(aa_on ? aa_size : 0);
+ uvs.resize(verts.size());
+ for (int i = 0; i < verts.size(); i++) {
+ uvs.write[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width;
+ uvs.write[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height;
+ }
+
+ // Draw stylebox.
+ RenderingServer *vs = RenderingServer::get_singleton();
+ vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors, uvs);
+}
+
+void StyleBoxFlat::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_bg_color", "color"), &StyleBoxFlat::set_bg_color);
+ ClassDB::bind_method(D_METHOD("get_bg_color"), &StyleBoxFlat::get_bg_color);
+
+ ClassDB::bind_method(D_METHOD("set_border_color", "color"), &StyleBoxFlat::set_border_color);
+ ClassDB::bind_method(D_METHOD("get_border_color"), &StyleBoxFlat::get_border_color);
+
+ ClassDB::bind_method(D_METHOD("set_border_width_all", "width"), &StyleBoxFlat::set_border_width_all);
+ ClassDB::bind_method(D_METHOD("get_border_width_min"), &StyleBoxFlat::get_border_width_min);
+
+ ClassDB::bind_method(D_METHOD("set_border_width", "margin", "width"), &StyleBoxFlat::set_border_width);
+ ClassDB::bind_method(D_METHOD("get_border_width", "margin"), &StyleBoxFlat::get_border_width);
+
+ ClassDB::bind_method(D_METHOD("set_border_blend", "blend"), &StyleBoxFlat::set_border_blend);
+ ClassDB::bind_method(D_METHOD("get_border_blend"), &StyleBoxFlat::get_border_blend);
+
+ ClassDB::bind_method(D_METHOD("set_corner_radius_all", "radius"), &StyleBoxFlat::set_corner_radius_all);
+
+ ClassDB::bind_method(D_METHOD("set_corner_radius", "corner", "radius"), &StyleBoxFlat::set_corner_radius);
+ ClassDB::bind_method(D_METHOD("get_corner_radius", "corner"), &StyleBoxFlat::get_corner_radius);
+
+ ClassDB::bind_method(D_METHOD("set_expand_margin", "margin", "size"), &StyleBoxFlat::set_expand_margin);
+ ClassDB::bind_method(D_METHOD("set_expand_margin_all", "size"), &StyleBoxFlat::set_expand_margin_all);
+ ClassDB::bind_method(D_METHOD("get_expand_margin", "margin"), &StyleBoxFlat::get_expand_margin);
+
+ ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &StyleBoxFlat::set_draw_center);
+ ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxFlat::is_draw_center_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_skew", "skew"), &StyleBoxFlat::set_skew);
+ ClassDB::bind_method(D_METHOD("get_skew"), &StyleBoxFlat::get_skew);
+
+ ClassDB::bind_method(D_METHOD("set_shadow_color", "color"), &StyleBoxFlat::set_shadow_color);
+ ClassDB::bind_method(D_METHOD("get_shadow_color"), &StyleBoxFlat::get_shadow_color);
+
+ ClassDB::bind_method(D_METHOD("set_shadow_size", "size"), &StyleBoxFlat::set_shadow_size);
+ ClassDB::bind_method(D_METHOD("get_shadow_size"), &StyleBoxFlat::get_shadow_size);
+
+ ClassDB::bind_method(D_METHOD("set_shadow_offset", "offset"), &StyleBoxFlat::set_shadow_offset);
+ ClassDB::bind_method(D_METHOD("get_shadow_offset"), &StyleBoxFlat::get_shadow_offset);
+
+ ClassDB::bind_method(D_METHOD("set_anti_aliased", "anti_aliased"), &StyleBoxFlat::set_anti_aliased);
+ ClassDB::bind_method(D_METHOD("is_anti_aliased"), &StyleBoxFlat::is_anti_aliased);
+
+ ClassDB::bind_method(D_METHOD("set_aa_size", "size"), &StyleBoxFlat::set_aa_size);
+ ClassDB::bind_method(D_METHOD("get_aa_size"), &StyleBoxFlat::get_aa_size);
+
+ ClassDB::bind_method(D_METHOD("set_corner_detail", "detail"), &StyleBoxFlat::set_corner_detail);
+ ClassDB::bind_method(D_METHOD("get_corner_detail"), &StyleBoxFlat::get_corner_detail);
+
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "bg_color"), "set_bg_color", "get_bg_color");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "skew"), "set_skew", "get_skew");
+
+ ADD_GROUP("Border Width", "border_width_");
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_border_width", "get_border_width", SIDE_BOTTOM);
+
+ ADD_GROUP("Border", "border_");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color"), "set_border_color", "get_border_color");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "border_blend"), "set_border_blend", "get_border_blend");
+
+ ADD_GROUP("Corner Radius", "corner_radius_");
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_TOP_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left", PROPERTY_HINT_RANGE, "0,1024,1,suffix:px"), "set_corner_radius", "get_corner_radius", CORNER_BOTTOM_LEFT);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "corner_detail", PROPERTY_HINT_RANGE, "1,20,1"), "set_corner_detail", "get_corner_detail");
+
+ ADD_GROUP("Expand Margins", "expand_margin_");
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM);
+
+ ADD_GROUP("Shadow", "shadow_");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset");
+
+ ADD_GROUP("Anti Aliasing", "anti_aliasing_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "anti_aliasing"), "set_anti_aliased", "is_anti_aliased");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anti_aliasing_size", PROPERTY_HINT_RANGE, "0.01,10,0.001,suffix:px"), "set_aa_size", "get_aa_size");
+}
+
+StyleBoxFlat::StyleBoxFlat() {}
+
+StyleBoxFlat::~StyleBoxFlat() {}
diff --git a/scene/resources/style_box_flat.h b/scene/resources/style_box_flat.h
new file mode 100644
index 0000000000..b6bb145f05
--- /dev/null
+++ b/scene/resources/style_box_flat.h
@@ -0,0 +1,118 @@
+/**************************************************************************/
+/* style_box_flat.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 STYLE_BOX_FLAT_H
+#define STYLE_BOX_FLAT_H
+
+#include "scene/resources/style_box.h"
+
+class StyleBoxFlat : public StyleBox {
+ GDCLASS(StyleBoxFlat, StyleBox);
+
+ Color bg_color = Color(0.6, 0.6, 0.6);
+ Color shadow_color = Color(0, 0, 0, 0.6);
+ Color border_color = Color(0.8, 0.8, 0.8);
+
+ real_t border_width[4] = {};
+ real_t expand_margin[4] = {};
+ real_t corner_radius[4] = {};
+
+ bool draw_center = true;
+ bool blend_border = false;
+ Vector2 skew;
+ bool anti_aliased = true;
+
+ int corner_detail = 8;
+ int shadow_size = 0;
+ Point2 shadow_offset;
+ real_t aa_size = 1;
+
+protected:
+ virtual float get_style_margin(Side p_side) const override;
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &p_property) const;
+
+public:
+ void set_bg_color(const Color &p_color);
+ Color get_bg_color() const;
+
+ void set_border_color(const Color &p_color);
+ Color get_border_color() const;
+
+ void set_border_width_all(int p_size);
+ int get_border_width_min() const;
+
+ void set_border_width(Side p_side, int p_width);
+ int get_border_width(Side p_side) const;
+
+ void set_border_blend(bool p_blend);
+ bool get_border_blend() const;
+
+ void set_corner_radius_all(int radius);
+ void set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left);
+ void set_corner_radius(Corner p_corner, const int radius);
+ int get_corner_radius(Corner p_corner) const;
+
+ void set_corner_detail(const int &p_corner_detail);
+ int get_corner_detail() const;
+
+ void set_expand_margin(Side p_expand_side, float p_size);
+ void set_expand_margin_all(float p_expand_margin_size);
+ void set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom);
+ float get_expand_margin(Side p_expand_side) const;
+
+ void set_draw_center(bool p_enabled);
+ bool is_draw_center_enabled() const;
+
+ void set_skew(Vector2 p_skew);
+ Vector2 get_skew() const;
+
+ void set_shadow_color(const Color &p_color);
+ Color get_shadow_color() const;
+
+ void set_shadow_size(const int &p_size);
+ int get_shadow_size() const;
+
+ void set_shadow_offset(const Point2 &p_offset);
+ Point2 get_shadow_offset() const;
+
+ void set_anti_aliased(const bool &p_anti_aliased);
+ bool is_anti_aliased() const;
+ void set_aa_size(const real_t p_aa_size);
+ real_t get_aa_size() const;
+
+ virtual Rect2 get_draw_rect(const Rect2 &p_rect) const override;
+ virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override;
+
+ StyleBoxFlat();
+ ~StyleBoxFlat();
+};
+
+#endif // STYLE_BOX_FLAT_H
diff --git a/scene/resources/style_box_line.cpp b/scene/resources/style_box_line.cpp
new file mode 100644
index 0000000000..9aeba88531
--- /dev/null
+++ b/scene/resources/style_box_line.cpp
@@ -0,0 +1,132 @@
+/**************************************************************************/
+/* style_box_line.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 "style_box_line.h"
+
+#include "servers/rendering_server.h"
+
+float StyleBoxLine::get_style_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0);
+
+ if (vertical) {
+ if (p_side == SIDE_LEFT || p_side == SIDE_RIGHT) {
+ return thickness / 2.0;
+ }
+ } else if (p_side == SIDE_TOP || p_side == SIDE_BOTTOM) {
+ return thickness / 2.0;
+ }
+
+ return 0;
+}
+
+void StyleBoxLine::set_color(const Color &p_color) {
+ color = p_color;
+ emit_changed();
+}
+
+Color StyleBoxLine::get_color() const {
+ return color;
+}
+
+void StyleBoxLine::set_thickness(int p_thickness) {
+ thickness = p_thickness;
+ emit_changed();
+}
+
+int StyleBoxLine::get_thickness() const {
+ return thickness;
+}
+
+void StyleBoxLine::set_vertical(bool p_vertical) {
+ vertical = p_vertical;
+ emit_changed();
+}
+
+bool StyleBoxLine::is_vertical() const {
+ return vertical;
+}
+
+void StyleBoxLine::set_grow_end(float p_grow_end) {
+ grow_end = p_grow_end;
+ emit_changed();
+}
+
+float StyleBoxLine::get_grow_end() const {
+ return grow_end;
+}
+
+void StyleBoxLine::set_grow_begin(float p_grow_begin) {
+ grow_begin = p_grow_begin;
+ emit_changed();
+}
+
+float StyleBoxLine::get_grow_begin() const {
+ return grow_begin;
+}
+
+void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const {
+ RenderingServer *vs = RenderingServer::get_singleton();
+ Rect2i r = p_rect;
+
+ if (vertical) {
+ r.position.y -= grow_begin;
+ r.size.y += (grow_begin + grow_end);
+ r.size.x = thickness;
+ } else {
+ r.position.x -= grow_begin;
+ r.size.x += (grow_begin + grow_end);
+ r.size.y = thickness;
+ }
+
+ vs->canvas_item_add_rect(p_canvas_item, r, color);
+}
+
+void StyleBoxLine::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_color", "color"), &StyleBoxLine::set_color);
+ ClassDB::bind_method(D_METHOD("get_color"), &StyleBoxLine::get_color);
+ ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &StyleBoxLine::set_thickness);
+ ClassDB::bind_method(D_METHOD("get_thickness"), &StyleBoxLine::get_thickness);
+ ClassDB::bind_method(D_METHOD("set_grow_begin", "offset"), &StyleBoxLine::set_grow_begin);
+ ClassDB::bind_method(D_METHOD("get_grow_begin"), &StyleBoxLine::get_grow_begin);
+ ClassDB::bind_method(D_METHOD("set_grow_end", "offset"), &StyleBoxLine::set_grow_end);
+ ClassDB::bind_method(D_METHOD("get_grow_end"), &StyleBoxLine::get_grow_end);
+ ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &StyleBoxLine::set_vertical);
+ ClassDB::bind_method(D_METHOD("is_vertical"), &StyleBoxLine::is_vertical);
+
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_begin", "get_grow_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1,suffix:px"), "set_grow_end", "get_grow_end");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,100,suffix:px"), "set_thickness", "get_thickness");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
+}
+
+StyleBoxLine::StyleBoxLine() {}
+
+StyleBoxLine::~StyleBoxLine() {}
diff --git a/scene/resources/style_box_line.h b/scene/resources/style_box_line.h
new file mode 100644
index 0000000000..18f765a1e4
--- /dev/null
+++ b/scene/resources/style_box_line.h
@@ -0,0 +1,70 @@
+/**************************************************************************/
+/* style_box_line.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 STYLE_BOX_LINE_H
+#define STYLE_BOX_LINE_H
+
+#include "scene/resources/style_box.h"
+
+class StyleBoxLine : public StyleBox {
+ GDCLASS(StyleBoxLine, StyleBox);
+ Color color;
+ int thickness = 1;
+ bool vertical = false;
+ float grow_begin = 1.0;
+ float grow_end = 1.0;
+
+protected:
+ virtual float get_style_margin(Side p_side) const override;
+ static void _bind_methods();
+
+public:
+ void set_color(const Color &p_color);
+ Color get_color() const;
+
+ void set_thickness(int p_thickness);
+ int get_thickness() const;
+
+ void set_vertical(bool p_vertical);
+ bool is_vertical() const;
+
+ void set_grow_begin(float p_grow);
+ float get_grow_begin() const;
+
+ void set_grow_end(float p_grow);
+ float get_grow_end() const;
+
+ virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override;
+
+ StyleBoxLine();
+ ~StyleBoxLine();
+};
+
+#endif // STYLE_BOX_LINE_H
diff --git a/scene/resources/style_box_texture.cpp b/scene/resources/style_box_texture.cpp
new file mode 100644
index 0000000000..156525d21c
--- /dev/null
+++ b/scene/resources/style_box_texture.cpp
@@ -0,0 +1,243 @@
+/**************************************************************************/
+/* style_box_texture.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 "style_box_texture.h"
+
+float StyleBoxTexture::get_style_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
+
+ return texture_margin[p_side];
+}
+
+void StyleBoxTexture::set_texture(Ref<Texture2D> p_texture) {
+ if (texture == p_texture) {
+ return;
+ }
+ texture = p_texture;
+ emit_changed();
+}
+
+Ref<Texture2D> StyleBoxTexture::get_texture() const {
+ return texture;
+}
+
+void StyleBoxTexture::set_texture_margin(Side p_side, float p_size) {
+ ERR_FAIL_INDEX((int)p_side, 4);
+
+ texture_margin[p_side] = p_size;
+ emit_changed();
+}
+
+void StyleBoxTexture::set_texture_margin_all(float p_size) {
+ for (int i = 0; i < 4; i++) {
+ texture_margin[i] = p_size;
+ }
+ emit_changed();
+}
+
+void StyleBoxTexture::set_texture_margin_individual(float p_left, float p_top, float p_right, float p_bottom) {
+ texture_margin[SIDE_LEFT] = p_left;
+ texture_margin[SIDE_TOP] = p_top;
+ texture_margin[SIDE_RIGHT] = p_right;
+ texture_margin[SIDE_BOTTOM] = p_bottom;
+ emit_changed();
+}
+
+float StyleBoxTexture::get_texture_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0.0);
+
+ return texture_margin[p_side];
+}
+
+void StyleBoxTexture::set_expand_margin(Side p_side, float p_size) {
+ ERR_FAIL_INDEX((int)p_side, 4);
+ expand_margin[p_side] = p_size;
+ emit_changed();
+}
+
+void StyleBoxTexture::set_expand_margin_all(float p_expand_margin_size) {
+ for (int i = 0; i < 4; i++) {
+ expand_margin[i] = p_expand_margin_size;
+ }
+ emit_changed();
+}
+
+void StyleBoxTexture::set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom) {
+ expand_margin[SIDE_LEFT] = p_left;
+ expand_margin[SIDE_TOP] = p_top;
+ expand_margin[SIDE_RIGHT] = p_right;
+ expand_margin[SIDE_BOTTOM] = p_bottom;
+ emit_changed();
+}
+
+float StyleBoxTexture::get_expand_margin(Side p_side) const {
+ ERR_FAIL_INDEX_V((int)p_side, 4, 0);
+ return expand_margin[p_side];
+}
+
+void StyleBoxTexture::set_region_rect(const Rect2 &p_region_rect) {
+ if (region_rect == p_region_rect) {
+ return;
+ }
+
+ region_rect = p_region_rect;
+ emit_changed();
+}
+
+Rect2 StyleBoxTexture::get_region_rect() const {
+ return region_rect;
+}
+
+void StyleBoxTexture::set_draw_center(bool p_enabled) {
+ draw_center = p_enabled;
+ emit_changed();
+}
+
+bool StyleBoxTexture::is_draw_center_enabled() const {
+ return draw_center;
+}
+
+void StyleBoxTexture::set_h_axis_stretch_mode(AxisStretchMode p_mode) {
+ ERR_FAIL_INDEX((int)p_mode, 3);
+ axis_h = p_mode;
+ emit_changed();
+}
+
+StyleBoxTexture::AxisStretchMode StyleBoxTexture::get_h_axis_stretch_mode() const {
+ return axis_h;
+}
+
+void StyleBoxTexture::set_v_axis_stretch_mode(AxisStretchMode p_mode) {
+ ERR_FAIL_INDEX((int)p_mode, 3);
+ axis_v = p_mode;
+ emit_changed();
+}
+
+StyleBoxTexture::AxisStretchMode StyleBoxTexture::get_v_axis_stretch_mode() const {
+ return axis_v;
+}
+
+void StyleBoxTexture::set_modulate(const Color &p_modulate) {
+ if (modulate == p_modulate) {
+ return;
+ }
+ modulate = p_modulate;
+ emit_changed();
+}
+
+Color StyleBoxTexture::get_modulate() const {
+ return modulate;
+}
+
+Rect2 StyleBoxTexture::get_draw_rect(const Rect2 &p_rect) const {
+ return p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]);
+}
+
+void StyleBoxTexture::draw(RID p_canvas_item, const Rect2 &p_rect) const {
+ if (texture.is_null()) {
+ return;
+ }
+
+ Rect2 rect = p_rect;
+ Rect2 src_rect = region_rect;
+
+ texture->get_rect_region(rect, src_rect, rect, src_rect);
+
+ rect.position.x -= expand_margin[SIDE_LEFT];
+ rect.position.y -= expand_margin[SIDE_TOP];
+ rect.size.x += expand_margin[SIDE_LEFT] + expand_margin[SIDE_RIGHT];
+ rect.size.y += expand_margin[SIDE_TOP] + expand_margin[SIDE_BOTTOM];
+
+ Vector2 start_offset = Vector2(texture_margin[SIDE_LEFT], texture_margin[SIDE_TOP]);
+ Vector2 end_offset = Vector2(texture_margin[SIDE_RIGHT], texture_margin[SIDE_BOTTOM]);
+
+ RenderingServer::get_singleton()->canvas_item_add_nine_patch(p_canvas_item, rect, src_rect, texture->get_rid(), start_offset, end_offset, RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center, modulate);
+}
+
+void StyleBoxTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &StyleBoxTexture::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &StyleBoxTexture::get_texture);
+
+ ClassDB::bind_method(D_METHOD("set_texture_margin", "margin", "size"), &StyleBoxTexture::set_texture_margin);
+ ClassDB::bind_method(D_METHOD("set_texture_margin_all", "size"), &StyleBoxTexture::set_texture_margin_all);
+ ClassDB::bind_method(D_METHOD("get_texture_margin", "margin"), &StyleBoxTexture::get_texture_margin);
+
+ ClassDB::bind_method(D_METHOD("set_expand_margin", "margin", "size"), &StyleBoxTexture::set_expand_margin);
+ ClassDB::bind_method(D_METHOD("set_expand_margin_all", "size"), &StyleBoxTexture::set_expand_margin_all);
+ ClassDB::bind_method(D_METHOD("get_expand_margin", "margin"), &StyleBoxTexture::get_expand_margin);
+
+ ClassDB::bind_method(D_METHOD("set_region_rect", "region"), &StyleBoxTexture::set_region_rect);
+ ClassDB::bind_method(D_METHOD("get_region_rect"), &StyleBoxTexture::get_region_rect);
+
+ ClassDB::bind_method(D_METHOD("set_draw_center", "enable"), &StyleBoxTexture::set_draw_center);
+ ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxTexture::is_draw_center_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_modulate", "color"), &StyleBoxTexture::set_modulate);
+ ClassDB::bind_method(D_METHOD("get_modulate"), &StyleBoxTexture::get_modulate);
+
+ ClassDB::bind_method(D_METHOD("set_h_axis_stretch_mode", "mode"), &StyleBoxTexture::set_h_axis_stretch_mode);
+ ClassDB::bind_method(D_METHOD("get_h_axis_stretch_mode"), &StyleBoxTexture::get_h_axis_stretch_mode);
+
+ ClassDB::bind_method(D_METHOD("set_v_axis_stretch_mode", "mode"), &StyleBoxTexture::set_v_axis_stretch_mode);
+ ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &StyleBoxTexture::get_v_axis_stretch_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
+
+ ADD_GROUP("Texture Margins", "texture_margin_");
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "texture_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_texture_margin", "get_texture_margin", SIDE_BOTTOM);
+
+ ADD_GROUP("Expand Margins", "expand_margin_");
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_LEFT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_TOP);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1,suffix:px"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM);
+
+ ADD_GROUP("Axis Stretch", "axis_stretch_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
+
+ ADD_GROUP("Sub-Region", "region_");
+ ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect", PROPERTY_HINT_NONE, "suffix:px"), "set_region_rect", "get_region_rect");
+
+ ADD_GROUP("Modulate", "modulate_");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate_color"), "set_modulate", "get_modulate");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled");
+
+ BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_STRETCH);
+ BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_TILE);
+ BIND_ENUM_CONSTANT(AXIS_STRETCH_MODE_TILE_FIT);
+}
+
+StyleBoxTexture::StyleBoxTexture() {}
+
+StyleBoxTexture::~StyleBoxTexture() {}
diff --git a/scene/resources/style_box_texture.h b/scene/resources/style_box_texture.h
new file mode 100644
index 0000000000..b1b833f470
--- /dev/null
+++ b/scene/resources/style_box_texture.h
@@ -0,0 +1,99 @@
+/**************************************************************************/
+/* style_box_texture.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 STYLE_BOX_TEXTURE_H
+#define STYLE_BOX_TEXTURE_H
+
+#include "scene/resources/style_box.h"
+#include "scene/resources/texture.h"
+
+class StyleBoxTexture : public StyleBox {
+ GDCLASS(StyleBoxTexture, StyleBox);
+
+public:
+ enum AxisStretchMode {
+ AXIS_STRETCH_MODE_STRETCH,
+ AXIS_STRETCH_MODE_TILE,
+ AXIS_STRETCH_MODE_TILE_FIT,
+ };
+
+private:
+ float expand_margin[4] = {};
+ float texture_margin[4] = {};
+ Rect2 region_rect;
+ Ref<Texture2D> texture;
+ bool draw_center = true;
+ Color modulate = Color(1, 1, 1, 1);
+ AxisStretchMode axis_h = AXIS_STRETCH_MODE_STRETCH;
+ AxisStretchMode axis_v = AXIS_STRETCH_MODE_STRETCH;
+
+protected:
+ virtual float get_style_margin(Side p_side) const override;
+ static void _bind_methods();
+
+public:
+ void set_texture(Ref<Texture2D> p_texture);
+ Ref<Texture2D> get_texture() const;
+
+ void set_texture_margin(Side p_side, float p_size);
+ void set_texture_margin_all(float p_size);
+ void set_texture_margin_individual(float p_left, float p_top, float p_right, float p_bottom);
+ float get_texture_margin(Side p_side) const;
+
+ void set_expand_margin(Side p_expand_side, float p_size);
+ void set_expand_margin_all(float p_expand_margin_size);
+ void set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom);
+ float get_expand_margin(Side p_expand_side) const;
+
+ void set_region_rect(const Rect2 &p_region_rect);
+ Rect2 get_region_rect() const;
+
+ void set_draw_center(bool p_enabled);
+ bool is_draw_center_enabled() const;
+
+ void set_h_axis_stretch_mode(AxisStretchMode p_mode);
+ AxisStretchMode get_h_axis_stretch_mode() const;
+
+ void set_v_axis_stretch_mode(AxisStretchMode p_mode);
+ AxisStretchMode get_v_axis_stretch_mode() const;
+
+ void set_modulate(const Color &p_modulate);
+ Color get_modulate() const;
+
+ virtual Rect2 get_draw_rect(const Rect2 &p_rect) const override;
+ virtual void draw(RID p_canvas_item, const Rect2 &p_rect) const override;
+
+ StyleBoxTexture();
+ ~StyleBoxTexture();
+};
+
+VARIANT_ENUM_CAST(StyleBoxTexture::AxisStretchMode)
+
+#endif // STYLE_BOX_TEXTURE_H
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 837aa39ce1..0efaad61fe 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -30,14 +30,7 @@
#include "texture.h"
-#include "core/core_string_names.h"
-#include "core/io/image_loader.h"
-#include "core/io/marshalls.h"
-#include "core/math/geometry_2d.h"
-#include "core/os/os.h"
-#include "scene/resources/bit_map.h"
-#include "scene/resources/mesh.h"
-#include "servers/camera/camera_feed.h"
+#include "scene/resources/placeholder_textures.h"
int Texture2D::get_width() const {
int ret = 0;
@@ -127,991 +120,6 @@ void Texture2D::_bind_methods() {
Texture2D::Texture2D() {
}
-/////////////////////
-
-void ImageTexture::reload_from_file() {
- String path = ResourceLoader::path_remap(get_path());
- if (!path.is_resource_file()) {
- return;
- }
-
- Ref<Image> img;
- img.instantiate();
-
- if (ImageLoader::load_image(path, img) == OK) {
- set_image(img);
- } else {
- Resource::reload_from_file();
- notify_property_list_changed();
- emit_changed();
- }
-}
-
-bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) {
- if (p_name == "image") {
- set_image(p_value);
- return true;
- }
- return false;
-}
-
-bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const {
- if (p_name == "image") {
- r_ret = get_image();
- return true;
- }
- return false;
-}
-
-void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("image"), PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT));
-}
-
-Ref<ImageTexture> ImageTexture::create_from_image(const Ref<Image> &p_image) {
- ERR_FAIL_COND_V_MSG(p_image.is_null(), Ref<ImageTexture>(), "Invalid image: null");
- ERR_FAIL_COND_V_MSG(p_image->is_empty(), Ref<ImageTexture>(), "Invalid image: image is empty");
-
- Ref<ImageTexture> image_texture;
- image_texture.instantiate();
- image_texture->set_image(p_image);
- return image_texture;
-}
-
-void ImageTexture::set_image(const Ref<Image> &p_image) {
- ERR_FAIL_COND_MSG(p_image.is_null() || p_image->is_empty(), "Invalid image");
- w = p_image->get_width();
- h = p_image->get_height();
- format = p_image->get_format();
- mipmaps = p_image->has_mipmaps();
-
- if (texture.is_null()) {
- texture = RenderingServer::get_singleton()->texture_2d_create(p_image);
- } else {
- RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_image);
- RenderingServer::get_singleton()->texture_replace(texture, new_texture);
- }
- notify_property_list_changed();
- emit_changed();
-
- image_stored = true;
-}
-
-Image::Format ImageTexture::get_format() const {
- return format;
-}
-
-void ImageTexture::update(const Ref<Image> &p_image) {
- ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image");
- ERR_FAIL_COND_MSG(texture.is_null(), "Texture is not initialized.");
- ERR_FAIL_COND_MSG(p_image->get_width() != w || p_image->get_height() != h,
- "The new image dimensions must match the texture size.");
- ERR_FAIL_COND_MSG(p_image->get_format() != format,
- "The new image format must match the texture's image format.");
- ERR_FAIL_COND_MSG(mipmaps != p_image->has_mipmaps(),
- "The new image mipmaps configuration must match the texture's image mipmaps configuration");
-
- RS::get_singleton()->texture_2d_update(texture, p_image);
-
- notify_property_list_changed();
- emit_changed();
-
- alpha_cache.unref();
- image_stored = true;
-}
-
-Ref<Image> ImageTexture::get_image() const {
- if (image_stored) {
- return RenderingServer::get_singleton()->texture_2d_get(texture);
- } else {
- return Ref<Image>();
- }
-}
-
-int ImageTexture::get_width() const {
- return w;
-}
-
-int ImageTexture::get_height() const {
- return h;
-}
-
-RID ImageTexture::get_rid() const {
- if (texture.is_null()) {
- //we are in trouble, create something temporary
- texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
- }
- return texture;
-}
-
-bool ImageTexture::has_alpha() const {
- return (format == Image::FORMAT_LA8 || format == Image::FORMAT_RGBA8);
-}
-
-void ImageTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
- if ((w | h) == 0) {
- return;
- }
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
-}
-
-void ImageTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
- if ((w | h) == 0) {
- return;
- }
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
-}
-
-void ImageTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
- if ((w | h) == 0) {
- return;
- }
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
-}
-
-bool ImageTexture::is_pixel_opaque(int p_x, int p_y) const {
- if (!alpha_cache.is_valid()) {
- Ref<Image> img = get_image();
- if (img.is_valid()) {
- if (img->is_compressed()) { //must decompress, if compressed
- Ref<Image> decom = img->duplicate();
- decom->decompress();
- img = decom;
- }
- alpha_cache.instantiate();
- alpha_cache->create_from_image_alpha(img);
- }
- }
-
- if (alpha_cache.is_valid()) {
- int aw = int(alpha_cache->get_size().width);
- int ah = int(alpha_cache->get_size().height);
- if (aw == 0 || ah == 0) {
- return true;
- }
-
- int x = p_x * aw / w;
- int y = p_y * ah / h;
-
- x = CLAMP(x, 0, aw);
- y = CLAMP(y, 0, ah);
-
- return alpha_cache->get_bit(x, y);
- }
-
- return true;
-}
-
-void ImageTexture::set_size_override(const Size2i &p_size) {
- Size2i s = p_size;
- if (s.x != 0) {
- w = s.x;
- }
- if (s.y != 0) {
- h = s.y;
- }
- RenderingServer::get_singleton()->texture_set_size_override(texture, w, h);
-}
-
-void ImageTexture::set_path(const String &p_path, bool p_take_over) {
- if (texture.is_valid()) {
- RenderingServer::get_singleton()->texture_set_path(texture, p_path);
- }
-
- Resource::set_path(p_path, p_take_over);
-}
-
-void ImageTexture::_bind_methods() {
- ClassDB::bind_static_method("ImageTexture", D_METHOD("create_from_image", "image"), &ImageTexture::create_from_image);
- ClassDB::bind_method(D_METHOD("get_format"), &ImageTexture::get_format);
-
- ClassDB::bind_method(D_METHOD("set_image", "image"), &ImageTexture::set_image);
- ClassDB::bind_method(D_METHOD("update", "image"), &ImageTexture::update);
- ClassDB::bind_method(D_METHOD("set_size_override", "size"), &ImageTexture::set_size_override);
-}
-
-ImageTexture::ImageTexture() {}
-
-ImageTexture::~ImageTexture() {
- if (texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RenderingServer::get_singleton()->free(texture);
- }
-}
-
-/////////////////////
-
-void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
- if (p_data.size() == 0) {
- return; //nothing to do
- }
-
- const uint8_t *data = p_data.ptr();
- uint32_t data_size = p_data.size();
- ERR_FAIL_COND(data_size < 20);
- compression_mode = CompressionMode(decode_uint32(data + 0));
- format = Image::Format(decode_uint32(data + 4));
- uint32_t mipmap_count = decode_uint32(data + 8);
- size.width = decode_uint32(data + 12);
- size.height = decode_uint32(data + 16);
- mipmaps = mipmap_count > 1;
-
- data += 20;
- data_size -= 20;
-
- Ref<Image> image;
-
- switch (compression_mode) {
- case COMPRESSION_MODE_LOSSLESS:
- case COMPRESSION_MODE_LOSSY: {
- Vector<uint8_t> image_data;
-
- ERR_FAIL_COND(data_size < 4);
- for (uint32_t i = 0; i < mipmap_count; i++) {
- uint32_t mipsize = decode_uint32(data);
- data += 4;
- data_size -= 4;
- ERR_FAIL_COND(mipsize < data_size);
- Ref<Image> img = memnew(Image(data, data_size));
- ERR_FAIL_COND(img->is_empty());
- if (img->get_format() != format) { // May happen due to webp/png in the tiny mipmaps.
- img->convert(format);
- }
- image_data.append_array(img->get_data());
-
- data += mipsize;
- data_size -= mipsize;
- }
-
- image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, image_data)));
-
- } break;
- case COMPRESSION_MODE_BASIS_UNIVERSAL: {
- ERR_FAIL_NULL(Image::basis_universal_unpacker_ptr);
- image = Image::basis_universal_unpacker_ptr(data, data_size);
-
- } break;
- case COMPRESSION_MODE_S3TC:
- case COMPRESSION_MODE_ETC2:
- case COMPRESSION_MODE_BPTC: {
- image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, p_data.slice(20))));
- } break;
- }
- ERR_FAIL_COND(image.is_null());
-
- if (texture.is_null()) {
- texture = RenderingServer::get_singleton()->texture_2d_create(image);
- } else {
- RID new_texture = RenderingServer::get_singleton()->texture_2d_create(image);
- RenderingServer::get_singleton()->texture_replace(texture, new_texture);
- }
-
- image_stored = true;
- RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
- alpha_cache.unref();
-
- if (keep_all_compressed_buffers || keep_compressed_buffer) {
- compressed_buffer = p_data;
- } else {
- compressed_buffer.clear();
- }
-}
-
-PortableCompressedTexture2D::CompressionMode PortableCompressedTexture2D::get_compression_mode() const {
- return compression_mode;
-}
-Vector<uint8_t> PortableCompressedTexture2D::_get_data() const {
- return compressed_buffer;
-}
-
-void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map, float p_lossy_quality) {
- ERR_FAIL_COND(p_image.is_null() || p_image->is_empty());
-
- Vector<uint8_t> buffer;
-
- buffer.resize(20);
- encode_uint32(p_compression_mode, buffer.ptrw());
- encode_uint32(p_image->get_format(), buffer.ptrw() + 4);
- encode_uint32(p_image->get_mipmap_count() + 1, buffer.ptrw() + 8);
- encode_uint32(p_image->get_width(), buffer.ptrw() + 12);
- encode_uint32(p_image->get_height(), buffer.ptrw() + 16);
-
- switch (p_compression_mode) {
- case COMPRESSION_MODE_LOSSLESS:
- case COMPRESSION_MODE_LOSSY: {
- for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
- Vector<uint8_t> data;
- if (p_compression_mode == COMPRESSION_MODE_LOSSY) {
- data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality);
- } else {
- data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i));
- }
- int data_len = data.size();
- buffer.resize(buffer.size() + 4);
- encode_uint32(data_len, buffer.ptrw() + buffer.size() - 4);
- buffer.append_array(data);
- }
- } break;
- case COMPRESSION_MODE_BASIS_UNIVERSAL: {
- Image::UsedChannels uc = p_image->detect_used_channels(p_normal_map ? Image::COMPRESS_SOURCE_NORMAL : Image::COMPRESS_SOURCE_GENERIC);
- Vector<uint8_t> budata = Image::basis_universal_packer(p_image, uc);
- buffer.append_array(budata);
-
- } break;
- case COMPRESSION_MODE_S3TC:
- case COMPRESSION_MODE_ETC2:
- case COMPRESSION_MODE_BPTC: {
- Ref<Image> copy = p_image->duplicate();
- switch (p_compression_mode) {
- case COMPRESSION_MODE_S3TC:
- copy->compress(Image::COMPRESS_S3TC);
- break;
- case COMPRESSION_MODE_ETC2:
- copy->compress(Image::COMPRESS_ETC2);
- break;
- case COMPRESSION_MODE_BPTC:
- copy->compress(Image::COMPRESS_BPTC);
- break;
- default: {
- };
- }
-
- buffer.append_array(copy->get_data());
-
- } break;
- }
-
- _set_data(buffer);
-}
-
-Image::Format PortableCompressedTexture2D::get_format() const {
- return format;
-}
-
-Ref<Image> PortableCompressedTexture2D::get_image() const {
- if (image_stored) {
- return RenderingServer::get_singleton()->texture_2d_get(texture);
- } else {
- return Ref<Image>();
- }
-}
-
-int PortableCompressedTexture2D::get_width() const {
- return size.width;
-}
-
-int PortableCompressedTexture2D::get_height() const {
- return size.height;
-}
-
-RID PortableCompressedTexture2D::get_rid() const {
- if (texture.is_null()) {
- //we are in trouble, create something temporary
- texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
- }
- return texture;
-}
-
-bool PortableCompressedTexture2D::has_alpha() const {
- return (format == Image::FORMAT_LA8 || format == Image::FORMAT_RGBA8);
-}
-
-void PortableCompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
- if (size.width == 0 || size.height == 0) {
- return;
- }
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, size), texture, false, p_modulate, p_transpose);
-}
-
-void PortableCompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
- if (size.width == 0 || size.height == 0) {
- return;
- }
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
-}
-
-void PortableCompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
- if (size.width == 0 || size.height == 0) {
- return;
- }
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
-}
-
-bool PortableCompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
- if (!alpha_cache.is_valid()) {
- Ref<Image> img = get_image();
- if (img.is_valid()) {
- if (img->is_compressed()) { //must decompress, if compressed
- Ref<Image> decom = img->duplicate();
- decom->decompress();
- img = decom;
- }
- alpha_cache.instantiate();
- alpha_cache->create_from_image_alpha(img);
- }
- }
-
- if (alpha_cache.is_valid()) {
- int aw = int(alpha_cache->get_size().width);
- int ah = int(alpha_cache->get_size().height);
- if (aw == 0 || ah == 0) {
- return true;
- }
-
- int x = p_x * aw / size.width;
- int y = p_y * ah / size.height;
-
- x = CLAMP(x, 0, aw);
- y = CLAMP(y, 0, ah);
-
- return alpha_cache->get_bit(x, y);
- }
-
- return true;
-}
-
-void PortableCompressedTexture2D::set_size_override(const Size2 &p_size) {
- size_override = p_size;
- RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
-}
-
-Size2 PortableCompressedTexture2D::get_size_override() const {
- return size_override;
-}
-
-void PortableCompressedTexture2D::set_path(const String &p_path, bool p_take_over) {
- if (texture.is_valid()) {
- RenderingServer::get_singleton()->texture_set_path(texture, p_path);
- }
-
- Resource::set_path(p_path, p_take_over);
-}
-
-bool PortableCompressedTexture2D::keep_all_compressed_buffers = false;
-
-void PortableCompressedTexture2D::set_keep_all_compressed_buffers(bool p_keep) {
- keep_all_compressed_buffers = p_keep;
-}
-
-bool PortableCompressedTexture2D::is_keeping_all_compressed_buffers() {
- return keep_all_compressed_buffers;
-}
-
-void PortableCompressedTexture2D::set_keep_compressed_buffer(bool p_keep) {
- keep_compressed_buffer = p_keep;
- if (!p_keep) {
- compressed_buffer.clear();
- }
-}
-
-bool PortableCompressedTexture2D::is_keeping_compressed_buffer() const {
- return keep_compressed_buffer;
-}
-
-void PortableCompressedTexture2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_from_image", "image", "compression_mode", "normal_map", "lossy_quality"), &PortableCompressedTexture2D::create_from_image, DEFVAL(false), DEFVAL(0.8));
- ClassDB::bind_method(D_METHOD("get_format"), &PortableCompressedTexture2D::get_format);
- ClassDB::bind_method(D_METHOD("get_compression_mode"), &PortableCompressedTexture2D::get_compression_mode);
-
- ClassDB::bind_method(D_METHOD("set_size_override", "size"), &PortableCompressedTexture2D::set_size_override);
- ClassDB::bind_method(D_METHOD("get_size_override"), &PortableCompressedTexture2D::get_size_override);
-
- ClassDB::bind_method(D_METHOD("set_keep_compressed_buffer", "keep"), &PortableCompressedTexture2D::set_keep_compressed_buffer);
- ClassDB::bind_method(D_METHOD("is_keeping_compressed_buffer"), &PortableCompressedTexture2D::is_keeping_compressed_buffer);
-
- ClassDB::bind_method(D_METHOD("_set_data", "data"), &PortableCompressedTexture2D::_set_data);
- ClassDB::bind_method(D_METHOD("_get_data"), &PortableCompressedTexture2D::_get_data);
-
- ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("set_keep_all_compressed_buffers", "keep"), &PortableCompressedTexture2D::set_keep_all_compressed_buffers);
- ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("is_keeping_all_compressed_buffers"), &PortableCompressedTexture2D::is_keeping_all_compressed_buffers);
-
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size_override", PROPERTY_HINT_NONE, "suffix:px"), "set_size_override", "get_size_override");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_compressed_buffer"), "set_keep_compressed_buffer", "is_keeping_compressed_buffer");
-
- BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSLESS);
- BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSY);
- BIND_ENUM_CONSTANT(COMPRESSION_MODE_BASIS_UNIVERSAL);
- BIND_ENUM_CONSTANT(COMPRESSION_MODE_S3TC);
- BIND_ENUM_CONSTANT(COMPRESSION_MODE_ETC2);
- BIND_ENUM_CONSTANT(COMPRESSION_MODE_BPTC);
-}
-
-PortableCompressedTexture2D::PortableCompressedTexture2D() {}
-
-PortableCompressedTexture2D::~PortableCompressedTexture2D() {
- if (texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RenderingServer::get_singleton()->free(texture);
- }
-}
-
-//////////////////////////////////////////
-
-Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_size_limit) {
- uint32_t data_format = f->get_32();
- uint32_t w = f->get_16();
- uint32_t h = f->get_16();
- uint32_t mipmaps = f->get_32();
- Image::Format format = Image::Format(f->get_32());
-
- if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP) {
- //look for a PNG or WebP file inside
-
- int sw = w;
- int sh = h;
-
- //mipmaps need to be read independently, they will be later combined
- Vector<Ref<Image>> mipmap_images;
- uint64_t total_size = 0;
-
- bool first = true;
-
- for (uint32_t i = 0; i < mipmaps + 1; i++) {
- uint32_t size = f->get_32();
-
- if (p_size_limit > 0 && i < (mipmaps - 1) && (sw > p_size_limit || sh > p_size_limit)) {
- //can't load this due to size limit
- sw = MAX(sw >> 1, 1);
- sh = MAX(sh >> 1, 1);
- f->seek(f->get_position() + size);
- continue;
- }
-
- Vector<uint8_t> pv;
- pv.resize(size);
- {
- uint8_t *wr = pv.ptrw();
- f->get_buffer(wr, size);
- }
-
- Ref<Image> img;
- if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) {
- img = Image::png_unpacker(pv);
- } else if (data_format == DATA_FORMAT_WEBP && Image::webp_unpacker) {
- img = Image::webp_unpacker(pv);
- }
-
- if (img.is_null() || img->is_empty()) {
- ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());
- }
-
- if (first) {
- //format will actually be the format of the first image,
- //as it may have changed on compression
- format = img->get_format();
- first = false;
- } else if (img->get_format() != format) {
- img->convert(format); //all needs to be the same format
- }
-
- total_size += img->get_data().size();
-
- mipmap_images.push_back(img);
-
- sw = MAX(sw >> 1, 1);
- sh = MAX(sh >> 1, 1);
- }
-
- //print_line("mipmap read total: " + itos(mipmap_images.size()));
-
- Ref<Image> image;
- image.instantiate();
-
- if (mipmap_images.size() == 1) {
- //only one image (which will most likely be the case anyway for this format)
- image = mipmap_images[0];
- return image;
-
- } else {
- //rarer use case, but needs to be supported
- Vector<uint8_t> img_data;
- img_data.resize(total_size);
-
- {
- uint8_t *wr = img_data.ptrw();
-
- int ofs = 0;
- for (int i = 0; i < mipmap_images.size(); i++) {
- Vector<uint8_t> id = mipmap_images[i]->get_data();
- int len = id.size();
- const uint8_t *r = id.ptr();
- memcpy(&wr[ofs], r, len);
- ofs += len;
- }
- }
-
- image->set_data(w, h, true, mipmap_images[0]->get_format(), img_data);
- return image;
- }
-
- } else if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) {
- int sw = w;
- int sh = h;
- uint32_t size = f->get_32();
- if (p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) {
- //can't load this due to size limit
- sw = MAX(sw >> 1, 1);
- sh = MAX(sh >> 1, 1);
- f->seek(f->get_position() + size);
- return Ref<Image>();
- }
- Vector<uint8_t> pv;
- pv.resize(size);
- {
- uint8_t *wr = pv.ptrw();
- f->get_buffer(wr, size);
- }
- Ref<Image> img;
- img = Image::basis_universal_unpacker(pv);
- if (img.is_null() || img->is_empty()) {
- ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref<Image>());
- }
- format = img->get_format();
- sw = MAX(sw >> 1, 1);
- sh = MAX(sh >> 1, 1);
- return img;
- } else if (data_format == DATA_FORMAT_IMAGE) {
- int size = Image::get_image_data_size(w, h, format, mipmaps ? true : false);
-
- for (uint32_t i = 0; i < mipmaps + 1; i++) {
- int tw, th;
- int ofs = Image::get_image_mipmap_offset_and_dimensions(w, h, format, i, tw, th);
-
- if (p_size_limit > 0 && i < mipmaps && (p_size_limit > tw || p_size_limit > th)) {
- if (ofs) {
- f->seek(f->get_position() + ofs);
- }
- continue; //oops, size limit enforced, go to next
- }
-
- Vector<uint8_t> data;
- data.resize(size - ofs);
-
- {
- uint8_t *wr = data.ptrw();
- f->get_buffer(wr, data.size());
- }
-
- Ref<Image> image = Image::create_from_data(tw, th, mipmaps - i ? true : false, format, data);
-
- return image;
- }
- }
-
- return Ref<Image>();
-}
-
-void CompressedTexture2D::set_path(const String &p_path, bool p_take_over) {
- if (texture.is_valid()) {
- RenderingServer::get_singleton()->texture_set_path(texture, p_path);
- }
-
- Resource::set_path(p_path, p_take_over);
-}
-
-void CompressedTexture2D::_requested_3d(void *p_ud) {
- CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
- Ref<CompressedTexture2D> ctex(ct);
- ERR_FAIL_NULL(request_3d_callback);
- request_3d_callback(ctex);
-}
-
-void CompressedTexture2D::_requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel) {
- CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
- Ref<CompressedTexture2D> ctex(ct);
- ERR_FAIL_NULL(request_roughness_callback);
- request_roughness_callback(ctex, p_normal_path, p_roughness_channel);
-}
-
-void CompressedTexture2D::_requested_normal(void *p_ud) {
- CompressedTexture2D *ct = (CompressedTexture2D *)p_ud;
- Ref<CompressedTexture2D> ctex(ct);
- ERR_FAIL_NULL(request_normal_callback);
- request_normal_callback(ctex);
-}
-
-CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_3d_callback = nullptr;
-CompressedTexture2D::TextureFormatRoughnessRequestCallback CompressedTexture2D::request_roughness_callback = nullptr;
-CompressedTexture2D::TextureFormatRequestCallback CompressedTexture2D::request_normal_callback = nullptr;
-
-Image::Format CompressedTexture2D::get_format() const {
- return format;
-}
-
-Error CompressedTexture2D::_load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
- alpha_cache.unref();
-
- ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER);
-
- Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
-
- uint8_t header[4];
- f->get_buffer(header, 4);
- if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != '2') {
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is corrupt (Bad header).");
- }
-
- uint32_t version = f->get_32();
-
- if (version > FORMAT_VERSION) {
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
- }
- r_width = f->get_32();
- r_height = f->get_32();
- uint32_t df = f->get_32(); //data format
-
- //skip reserved
- mipmap_limit = int(f->get_32());
- //reserved
- f->get_32();
- f->get_32();
- f->get_32();
-
-#ifdef TOOLS_ENABLED
-
- r_request_3d = request_3d_callback && df & FORMAT_BIT_DETECT_3D;
- r_request_roughness = request_roughness_callback && df & FORMAT_BIT_DETECT_ROUGNESS;
- r_request_normal = request_normal_callback && df & FORMAT_BIT_DETECT_NORMAL;
-
-#else
-
- r_request_3d = false;
- r_request_roughness = false;
- r_request_normal = false;
-
-#endif
- if (!(df & FORMAT_BIT_STREAM)) {
- p_size_limit = 0;
- }
-
- image = load_image_from_file(f, p_size_limit);
-
- if (image.is_null() || image->is_empty()) {
- return ERR_CANT_OPEN;
- }
-
- return OK;
-}
-
-Error CompressedTexture2D::load(const String &p_path) {
- int lw, lh;
- Ref<Image> image;
- image.instantiate();
-
- bool request_3d;
- bool request_normal;
- bool request_roughness;
- int mipmap_limit;
-
- Error err = _load_data(p_path, lw, lh, image, request_3d, request_normal, request_roughness, mipmap_limit);
- if (err) {
- return err;
- }
-
- if (texture.is_valid()) {
- RID new_texture = RS::get_singleton()->texture_2d_create(image);
- RS::get_singleton()->texture_replace(texture, new_texture);
- } else {
- texture = RS::get_singleton()->texture_2d_create(image);
- }
- if (lw || lh) {
- RS::get_singleton()->texture_set_size_override(texture, lw, lh);
- }
-
- w = lw;
- h = lh;
- path_to_file = p_path;
- format = image->get_format();
-
- if (get_path().is_empty()) {
- //temporarily set path if no path set for resource, helps find errors
- RenderingServer::get_singleton()->texture_set_path(texture, p_path);
- }
-
-#ifdef TOOLS_ENABLED
-
- if (request_3d) {
- //print_line("request detect 3D at " + p_path);
- RS::get_singleton()->texture_set_detect_3d_callback(texture, _requested_3d, this);
- } else {
- //print_line("not requesting detect 3D at " + p_path);
- RS::get_singleton()->texture_set_detect_3d_callback(texture, nullptr, nullptr);
- }
-
- if (request_roughness) {
- //print_line("request detect srgb at " + p_path);
- RS::get_singleton()->texture_set_detect_roughness_callback(texture, _requested_roughness, this);
- } else {
- //print_line("not requesting detect srgb at " + p_path);
- RS::get_singleton()->texture_set_detect_roughness_callback(texture, nullptr, nullptr);
- }
-
- if (request_normal) {
- //print_line("request detect srgb at " + p_path);
- RS::get_singleton()->texture_set_detect_normal_callback(texture, _requested_normal, this);
- } else {
- //print_line("not requesting detect normal at " + p_path);
- RS::get_singleton()->texture_set_detect_normal_callback(texture, nullptr, nullptr);
- }
-
-#endif
- notify_property_list_changed();
- emit_changed();
- return OK;
-}
-
-String CompressedTexture2D::get_load_path() const {
- return path_to_file;
-}
-
-int CompressedTexture2D::get_width() const {
- return w;
-}
-
-int CompressedTexture2D::get_height() const {
- return h;
-}
-
-RID CompressedTexture2D::get_rid() const {
- if (!texture.is_valid()) {
- texture = RS::get_singleton()->texture_2d_placeholder_create();
- }
- return texture;
-}
-
-void CompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
- if ((w | h) == 0) {
- return;
- }
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, Size2(w, h)), texture, false, p_modulate, p_transpose);
-}
-
-void CompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
- if ((w | h) == 0) {
- return;
- }
- RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
-}
-
-void CompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
- if ((w | h) == 0) {
- return;
- }
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
-}
-
-bool CompressedTexture2D::has_alpha() const {
- return false;
-}
-
-Ref<Image> CompressedTexture2D::get_image() const {
- if (texture.is_valid()) {
- return RS::get_singleton()->texture_2d_get(texture);
- } else {
- return Ref<Image>();
- }
-}
-
-bool CompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
- if (!alpha_cache.is_valid()) {
- Ref<Image> img = get_image();
- if (img.is_valid()) {
- if (img->is_compressed()) { //must decompress, if compressed
- Ref<Image> decom = img->duplicate();
- decom->decompress();
- img = decom;
- }
-
- alpha_cache.instantiate();
- alpha_cache->create_from_image_alpha(img);
- }
- }
-
- if (alpha_cache.is_valid()) {
- int aw = int(alpha_cache->get_size().width);
- int ah = int(alpha_cache->get_size().height);
- if (aw == 0 || ah == 0) {
- return true;
- }
-
- int x = p_x * aw / w;
- int y = p_y * ah / h;
-
- x = CLAMP(x, 0, aw);
- y = CLAMP(y, 0, ah);
-
- return alpha_cache->get_bit(x, y);
- }
-
- return true;
-}
-
-void CompressedTexture2D::reload_from_file() {
- String path = get_path();
- if (!path.is_resource_file()) {
- return;
- }
-
- path = ResourceLoader::path_remap(path); //remap for translation
- path = ResourceLoader::import_remap(path); //remap for import
- if (!path.is_resource_file()) {
- return;
- }
-
- load(path);
-}
-
-void CompressedTexture2D::_validate_property(PropertyInfo &p_property) const {
-}
-
-void CompressedTexture2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture2D::load);
- ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture2D::get_load_path);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
-}
-
-CompressedTexture2D::CompressedTexture2D() {}
-
-CompressedTexture2D::~CompressedTexture2D() {
- if (texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(texture);
- }
-}
-
-Ref<Resource> ResourceFormatLoaderCompressedTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- Ref<CompressedTexture2D> st;
- st.instantiate();
- Error err = st->load(p_path);
- if (r_error) {
- *r_error = err;
- }
- if (err != OK) {
- return Ref<Resource>();
- }
-
- return st;
-}
-
-void ResourceFormatLoaderCompressedTexture2D::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("ctex");
-}
-
-bool ResourceFormatLoaderCompressedTexture2D::handles_type(const String &p_type) const {
- return p_type == "CompressedTexture2D";
-}
-
-String ResourceFormatLoaderCompressedTexture2D::get_resource_type(const String &p_path) const {
- if (p_path.get_extension().to_lower() == "ctex") {
- return "CompressedTexture2D";
- }
- return "";
-}
-
-////////////////////////////////////
-
TypedArray<Image> Texture3D::_get_datai() const {
Vector<Ref<Image>> data = get_data();
@@ -1163,6 +171,7 @@ Vector<Ref<Image>> Texture3D::get_data() const {
}
return data;
}
+
void Texture3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_format"), &Texture3D::get_format);
ClassDB::bind_method(D_METHOD("get_width"), &Texture3D::get_width);
@@ -1187,1652 +196,6 @@ Ref<Resource> Texture3D::create_placeholder() const {
return placeholder;
}
-//////////////////////////////////////////
-
-Image::Format ImageTexture3D::get_format() const {
- return format;
-}
-int ImageTexture3D::get_width() const {
- return width;
-}
-int ImageTexture3D::get_height() const {
- return height;
-}
-int ImageTexture3D::get_depth() const {
- return depth;
-}
-bool ImageTexture3D::has_mipmaps() const {
- return mipmaps;
-}
-
-Error ImageTexture3D::_create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const TypedArray<Image> &p_data) {
- Vector<Ref<Image>> images;
- images.resize(p_data.size());
- for (int i = 0; i < images.size(); i++) {
- images.write[i] = p_data[i];
- }
- return create(p_format, p_width, p_height, p_depth, p_mipmaps, images);
-}
-
-void ImageTexture3D::_update(const TypedArray<Image> &p_data) {
- Vector<Ref<Image>> images;
- images.resize(p_data.size());
- for (int i = 0; i < images.size(); i++) {
- images.write[i] = p_data[i];
- }
- return update(images);
-}
-
-Error ImageTexture3D::create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) {
- RID tex = RenderingServer::get_singleton()->texture_3d_create(p_format, p_width, p_height, p_depth, p_mipmaps, p_data);
- ERR_FAIL_COND_V(tex.is_null(), ERR_CANT_CREATE);
-
- if (texture.is_valid()) {
- RenderingServer::get_singleton()->texture_replace(texture, tex);
- } else {
- texture = tex;
- }
-
- format = p_format;
- width = p_width;
- height = p_height;
- depth = p_depth;
- mipmaps = p_mipmaps;
-
- return OK;
-}
-
-void ImageTexture3D::update(const Vector<Ref<Image>> &p_data) {
- ERR_FAIL_COND(!texture.is_valid());
- RenderingServer::get_singleton()->texture_3d_update(texture, p_data);
-}
-
-Vector<Ref<Image>> ImageTexture3D::get_data() const {
- ERR_FAIL_COND_V(!texture.is_valid(), Vector<Ref<Image>>());
- return RS::get_singleton()->texture_3d_get(texture);
-}
-
-RID ImageTexture3D::get_rid() const {
- if (!texture.is_valid()) {
- texture = RS::get_singleton()->texture_3d_placeholder_create();
- }
- return texture;
-}
-void ImageTexture3D::set_path(const String &p_path, bool p_take_over) {
- if (texture.is_valid()) {
- RenderingServer::get_singleton()->texture_set_path(texture, p_path);
- }
-
- Resource::set_path(p_path, p_take_over);
-}
-
-void ImageTexture3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create", "format", "width", "height", "depth", "use_mipmaps", "data"), &ImageTexture3D::_create);
- ClassDB::bind_method(D_METHOD("update", "data"), &ImageTexture3D::_update);
-}
-
-ImageTexture3D::ImageTexture3D() {
-}
-
-ImageTexture3D::~ImageTexture3D() {
- if (texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(texture);
- }
-}
-
-////////////////////////////////////////////
-
-void CompressedTexture3D::set_path(const String &p_path, bool p_take_over) {
- if (texture.is_valid()) {
- RenderingServer::get_singleton()->texture_set_path(texture, p_path);
- }
-
- Resource::set_path(p_path, p_take_over);
-}
-
-Image::Format CompressedTexture3D::get_format() const {
- return format;
-}
-
-Error CompressedTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) {
- Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
-
- uint8_t header[4];
- f->get_buffer(header, 4);
- ERR_FAIL_COND_V(header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L', ERR_FILE_UNRECOGNIZED);
-
- //stored as compressed textures (used for lossless and lossy compression)
- uint32_t version = f->get_32();
-
- if (version > FORMAT_VERSION) {
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
- }
-
- r_depth = f->get_32(); //depth
- f->get_32(); //ignored (mode)
- f->get_32(); // ignored (data format)
-
- f->get_32(); //ignored
- int mipmap_count = f->get_32();
- f->get_32(); //ignored
- f->get_32(); //ignored
-
- r_mipmaps = mipmap_count != 0;
-
- r_data.clear();
-
- for (int i = 0; i < (r_depth + mipmap_count); i++) {
- Ref<Image> image = CompressedTexture2D::load_image_from_file(f, 0);
- ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
- if (i == 0) {
- r_format = image->get_format();
- r_width = image->get_width();
- r_height = image->get_height();
- }
- r_data.push_back(image);
- }
-
- return OK;
-}
-
-Error CompressedTexture3D::load(const String &p_path) {
- Vector<Ref<Image>> data;
-
- int tw, th, td;
- Image::Format tfmt;
- bool tmm;
-
- Error err = _load_data(p_path, data, tfmt, tw, th, td, tmm);
- if (err) {
- return err;
- }
-
- if (texture.is_valid()) {
- RID new_texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data);
- RS::get_singleton()->texture_replace(texture, new_texture);
- } else {
- texture = RS::get_singleton()->texture_3d_create(tfmt, tw, th, td, tmm, data);
- }
-
- w = tw;
- h = th;
- d = td;
- mipmaps = tmm;
- format = tfmt;
-
- path_to_file = p_path;
-
- if (get_path().is_empty()) {
- //temporarily set path if no path set for resource, helps find errors
- RenderingServer::get_singleton()->texture_set_path(texture, p_path);
- }
-
- notify_property_list_changed();
- emit_changed();
- return OK;
-}
-
-String CompressedTexture3D::get_load_path() const {
- return path_to_file;
-}
-
-int CompressedTexture3D::get_width() const {
- return w;
-}
-
-int CompressedTexture3D::get_height() const {
- return h;
-}
-
-int CompressedTexture3D::get_depth() const {
- return d;
-}
-
-bool CompressedTexture3D::has_mipmaps() const {
- return mipmaps;
-}
-
-RID CompressedTexture3D::get_rid() const {
- if (!texture.is_valid()) {
- texture = RS::get_singleton()->texture_3d_placeholder_create();
- }
- return texture;
-}
-
-Vector<Ref<Image>> CompressedTexture3D::get_data() const {
- if (texture.is_valid()) {
- return RS::get_singleton()->texture_3d_get(texture);
- } else {
- return Vector<Ref<Image>>();
- }
-}
-
-void CompressedTexture3D::reload_from_file() {
- String path = get_path();
- if (!path.is_resource_file()) {
- return;
- }
-
- path = ResourceLoader::path_remap(path); //remap for translation
- path = ResourceLoader::import_remap(path); //remap for import
- if (!path.is_resource_file()) {
- return;
- }
-
- load(path);
-}
-
-void CompressedTexture3D::_validate_property(PropertyInfo &p_property) const {
-}
-
-void CompressedTexture3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTexture3D::load);
- ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTexture3D::get_load_path);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
-}
-
-CompressedTexture3D::CompressedTexture3D() {}
-
-CompressedTexture3D::~CompressedTexture3D() {
- if (texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(texture);
- }
-}
-
-/////////////////////////////
-
-Ref<Resource> ResourceFormatLoaderCompressedTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- Ref<CompressedTexture3D> st;
- st.instantiate();
- Error err = st->load(p_path);
- if (r_error) {
- *r_error = err;
- }
- if (err != OK) {
- return Ref<Resource>();
- }
-
- return st;
-}
-
-void ResourceFormatLoaderCompressedTexture3D::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("ctex3d");
-}
-
-bool ResourceFormatLoaderCompressedTexture3D::handles_type(const String &p_type) const {
- return p_type == "CompressedTexture3D";
-}
-
-String ResourceFormatLoaderCompressedTexture3D::get_resource_type(const String &p_path) const {
- if (p_path.get_extension().to_lower() == "ctex3d") {
- return "CompressedTexture3D";
- }
- return "";
-}
-
-////////////////////////////////////////////
-
-int AtlasTexture::get_width() const {
- if (region.size.width == 0) {
- if (atlas.is_valid()) {
- return atlas->get_width();
- }
- return 1;
- } else {
- return region.size.width + margin.size.width;
- }
-}
-
-int AtlasTexture::get_height() const {
- if (region.size.height == 0) {
- if (atlas.is_valid()) {
- return atlas->get_height();
- }
- return 1;
- } else {
- return region.size.height + margin.size.height;
- }
-}
-
-RID AtlasTexture::get_rid() const {
- if (atlas.is_valid()) {
- return atlas->get_rid();
- }
-
- return RID();
-}
-
-bool AtlasTexture::has_alpha() const {
- if (atlas.is_valid()) {
- return atlas->has_alpha();
- }
-
- return false;
-}
-
-void AtlasTexture::set_atlas(const Ref<Texture2D> &p_atlas) {
- ERR_FAIL_COND(p_atlas == this);
- if (atlas == p_atlas) {
- return;
- }
- // Support recursive AtlasTextures.
- if (Ref<AtlasTexture>(atlas).is_valid()) {
- atlas->disconnect(CoreStringNames::get_singleton()->changed, callable_mp((Resource *)this, &AtlasTexture::emit_changed));
- }
- atlas = p_atlas;
- if (Ref<AtlasTexture>(atlas).is_valid()) {
- atlas->connect(CoreStringNames::get_singleton()->changed, callable_mp((Resource *)this, &AtlasTexture::emit_changed));
- }
-
- emit_changed();
-}
-
-Ref<Texture2D> AtlasTexture::get_atlas() const {
- return atlas;
-}
-
-void AtlasTexture::set_region(const Rect2 &p_region) {
- if (region == p_region) {
- return;
- }
- region = p_region;
- emit_changed();
-}
-
-Rect2 AtlasTexture::get_region() const {
- return region;
-}
-
-void AtlasTexture::set_margin(const Rect2 &p_margin) {
- if (margin == p_margin) {
- return;
- }
- margin = p_margin;
- emit_changed();
-}
-
-Rect2 AtlasTexture::get_margin() const {
- return margin;
-}
-
-void AtlasTexture::set_filter_clip(const bool p_enable) {
- filter_clip = p_enable;
- emit_changed();
-}
-
-bool AtlasTexture::has_filter_clip() const {
- return filter_clip;
-}
-
-void AtlasTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_atlas", "atlas"), &AtlasTexture::set_atlas);
- ClassDB::bind_method(D_METHOD("get_atlas"), &AtlasTexture::get_atlas);
-
- ClassDB::bind_method(D_METHOD("set_region", "region"), &AtlasTexture::set_region);
- ClassDB::bind_method(D_METHOD("get_region"), &AtlasTexture::get_region);
-
- ClassDB::bind_method(D_METHOD("set_margin", "margin"), &AtlasTexture::set_margin);
- ClassDB::bind_method(D_METHOD("get_margin"), &AtlasTexture::get_margin);
-
- ClassDB::bind_method(D_METHOD("set_filter_clip", "enable"), &AtlasTexture::set_filter_clip);
- ClassDB::bind_method(D_METHOD("has_filter_clip"), &AtlasTexture::has_filter_clip);
-
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "atlas", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_atlas", "get_atlas");
- ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region", PROPERTY_HINT_NONE, "suffix:px"), "set_region", "get_region");
- ADD_PROPERTY(PropertyInfo(Variant::RECT2, "margin", PROPERTY_HINT_NONE, "suffix:px"), "set_margin", "get_margin");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_clip"), "set_filter_clip", "has_filter_clip");
-}
-
-void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
- if (!atlas.is_valid()) {
- return;
- }
-
- Rect2 rc = region;
-
- if (rc.size.width == 0) {
- rc.size.width = atlas->get_width();
- }
-
- if (rc.size.height == 0) {
- rc.size.height = atlas->get_height();
- }
-
- atlas->draw_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), rc, p_modulate, p_transpose, filter_clip);
-}
-
-void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
- if (!atlas.is_valid()) {
- return;
- }
-
- Rect2 rc = region;
-
- if (rc.size.width == 0) {
- rc.size.width = atlas->get_width();
- }
-
- if (rc.size.height == 0) {
- rc.size.height = atlas->get_height();
- }
-
- Vector2 scale = p_rect.size / (region.size + margin.size);
- Rect2 dr(p_rect.position + margin.position * scale, rc.size * scale);
-
- atlas->draw_rect_region(p_canvas_item, dr, rc, p_modulate, p_transpose, filter_clip);
-}
-
-void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
- //this might not necessarily work well if using a rect, needs to be fixed properly
- if (!atlas.is_valid()) {
- return;
- }
-
- Rect2 dr;
- Rect2 src_c;
- get_rect_region(p_rect, p_src_rect, dr, src_c);
-
- atlas->draw_rect_region(p_canvas_item, dr, src_c, p_modulate, p_transpose, filter_clip);
-}
-
-bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
- if (!atlas.is_valid()) {
- return false;
- }
-
- Rect2 src = p_src_rect;
- if (src.size == Size2()) {
- src.size = region.size;
- }
- Vector2 scale = p_rect.size / src.size;
-
- src.position += (region.position - margin.position);
- Rect2 src_clipped = region.intersection(src);
- if (src_clipped.size == Size2()) {
- return false;
- }
-
- Vector2 ofs = (src_clipped.position - src.position);
- if (scale.x < 0) {
- ofs.x += (src_clipped.size.x - src.size.x);
- }
- if (scale.y < 0) {
- ofs.y += (src_clipped.size.y - src.size.y);
- }
-
- r_rect = Rect2(p_rect.position + ofs * scale, src_clipped.size * scale);
- r_src_rect = src_clipped;
- return true;
-}
-
-bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const {
- if (!atlas.is_valid()) {
- return true;
- }
-
- int x = p_x + region.position.x - margin.position.x;
- int y = p_y + region.position.y - margin.position.y;
-
- // margin edge may outside of atlas
- if (x < 0 || x >= atlas->get_width()) {
- return false;
- }
- if (y < 0 || y >= atlas->get_height()) {
- return false;
- }
-
- return atlas->is_pixel_opaque(x, y);
-}
-
-Ref<Image> AtlasTexture::get_image() const {
- if (!atlas.is_valid() || !atlas->get_image().is_valid()) {
- return Ref<Image>();
- }
-
- return atlas->get_image()->get_region(region);
-}
-
-AtlasTexture::AtlasTexture() {}
-
-/////////////////////////////////////////
-
-int MeshTexture::get_width() const {
- return size.width;
-}
-
-int MeshTexture::get_height() const {
- return size.height;
-}
-
-RID MeshTexture::get_rid() const {
- return RID();
-}
-
-bool MeshTexture::has_alpha() const {
- return false;
-}
-
-void MeshTexture::set_mesh(const Ref<Mesh> &p_mesh) {
- mesh = p_mesh;
-}
-
-Ref<Mesh> MeshTexture::get_mesh() const {
- return mesh;
-}
-
-void MeshTexture::set_image_size(const Size2 &p_size) {
- size = p_size;
-}
-
-Size2 MeshTexture::get_image_size() const {
- return size;
-}
-
-void MeshTexture::set_base_texture(const Ref<Texture2D> &p_texture) {
- base_texture = p_texture;
-}
-
-Ref<Texture2D> MeshTexture::get_base_texture() const {
- return base_texture;
-}
-
-void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
- if (mesh.is_null() || base_texture.is_null()) {
- return;
- }
- Transform2D xform;
- xform.set_origin(p_pos);
- if (p_transpose) {
- SWAP(xform.columns[0][1], xform.columns[1][0]);
- SWAP(xform.columns[0][0], xform.columns[1][1]);
- }
- RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
-}
-
-void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
- if (mesh.is_null() || base_texture.is_null()) {
- return;
- }
- Transform2D xform;
- Vector2 origin = p_rect.position;
- if (p_rect.size.x < 0) {
- origin.x += size.x;
- }
- if (p_rect.size.y < 0) {
- origin.y += size.y;
- }
- xform.set_origin(origin);
- xform.set_scale(p_rect.size / size);
-
- if (p_transpose) {
- SWAP(xform.columns[0][1], xform.columns[1][0]);
- SWAP(xform.columns[0][0], xform.columns[1][1]);
- }
- RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
-}
-
-void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
- if (mesh.is_null() || base_texture.is_null()) {
- return;
- }
- Transform2D xform;
- Vector2 origin = p_rect.position;
- if (p_rect.size.x < 0) {
- origin.x += size.x;
- }
- if (p_rect.size.y < 0) {
- origin.y += size.y;
- }
- xform.set_origin(origin);
- xform.set_scale(p_rect.size / size);
-
- if (p_transpose) {
- SWAP(xform.columns[0][1], xform.columns[1][0]);
- SWAP(xform.columns[0][0], xform.columns[1][1]);
- }
- RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid());
-}
-
-bool MeshTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
- r_rect = p_rect;
- r_src_rect = p_src_rect;
- return true;
-}
-
-bool MeshTexture::is_pixel_opaque(int p_x, int p_y) const {
- return true;
-}
-
-void MeshTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &MeshTexture::set_mesh);
- ClassDB::bind_method(D_METHOD("get_mesh"), &MeshTexture::get_mesh);
- ClassDB::bind_method(D_METHOD("set_image_size", "size"), &MeshTexture::set_image_size);
- ClassDB::bind_method(D_METHOD("get_image_size"), &MeshTexture::get_image_size);
- ClassDB::bind_method(D_METHOD("set_base_texture", "texture"), &MeshTexture::set_base_texture);
- ClassDB::bind_method(D_METHOD("get_base_texture"), &MeshTexture::get_base_texture);
-
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_base_texture", "get_base_texture");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "image_size", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_image_size", "get_image_size");
-}
-
-MeshTexture::MeshTexture() {
-}
-
-//////////////////////////////////////////
-
-void CurveTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width);
-
- ClassDB::bind_method(D_METHOD("set_curve", "curve"), &CurveTexture::set_curve);
- ClassDB::bind_method(D_METHOD("get_curve"), &CurveTexture::get_curve);
-
- ClassDB::bind_method(D_METHOD("set_texture_mode", "texture_mode"), &CurveTexture::set_texture_mode);
- ClassDB::bind_method(D_METHOD("get_texture_mode"), &CurveTexture::get_texture_mode);
-
- ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096,suffix:px"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "RGB,Red"), "set_texture_mode", "get_texture_mode");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
-
- BIND_ENUM_CONSTANT(TEXTURE_MODE_RGB);
- BIND_ENUM_CONSTANT(TEXTURE_MODE_RED);
-}
-
-void CurveTexture::set_width(int p_width) {
- ERR_FAIL_COND(p_width < 32 || p_width > 4096);
-
- if (_width == p_width) {
- return;
- }
-
- _width = p_width;
- _update();
-}
-
-int CurveTexture::get_width() const {
- return _width;
-}
-
-void CurveTexture::ensure_default_setup(float p_min, float p_max) {
- if (_curve.is_null()) {
- Ref<Curve> curve = Ref<Curve>(memnew(Curve));
- curve->add_point(Vector2(0, 1));
- curve->add_point(Vector2(1, 1));
- curve->set_min_value(p_min);
- curve->set_max_value(p_max);
- set_curve(curve);
- // Min and max is 0..1 by default
- }
-}
-
-void CurveTexture::set_curve(Ref<Curve> p_curve) {
- if (_curve != p_curve) {
- if (_curve.is_valid()) {
- _curve->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveTexture::_update));
- }
- _curve = p_curve;
- if (_curve.is_valid()) {
- _curve->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveTexture::_update));
- }
- _update();
- }
-}
-
-void CurveTexture::_update() {
- Vector<uint8_t> data;
- data.resize(_width * sizeof(float) * (texture_mode == TEXTURE_MODE_RGB ? 3 : 1));
-
- // The array is locked in that scope
- {
- uint8_t *wd8 = data.ptrw();
- float *wd = (float *)wd8;
-
- if (_curve.is_valid()) {
- Curve &curve = **_curve;
- for (int i = 0; i < _width; ++i) {
- float t = i / static_cast<float>(_width);
- if (texture_mode == TEXTURE_MODE_RGB) {
- wd[i * 3 + 0] = curve.sample_baked(t);
- wd[i * 3 + 1] = wd[i * 3 + 0];
- wd[i * 3 + 2] = wd[i * 3 + 0];
- } else {
- wd[i] = curve.sample_baked(t);
- }
- }
-
- } else {
- for (int i = 0; i < _width; ++i) {
- if (texture_mode == TEXTURE_MODE_RGB) {
- wd[i * 3 + 0] = 0;
- wd[i * 3 + 1] = 0;
- wd[i * 3 + 2] = 0;
- } else {
- wd[i] = 0;
- }
- }
- }
- }
-
- Ref<Image> image = memnew(Image(_width, 1, false, texture_mode == TEXTURE_MODE_RGB ? Image::FORMAT_RGBF : Image::FORMAT_RF, data));
-
- if (_texture.is_valid()) {
- if (_current_texture_mode != texture_mode || _current_width != _width) {
- RID new_texture = RS::get_singleton()->texture_2d_create(image);
- RS::get_singleton()->texture_replace(_texture, new_texture);
- } else {
- RS::get_singleton()->texture_2d_update(_texture, image);
- }
- } else {
- _texture = RS::get_singleton()->texture_2d_create(image);
- }
- _current_texture_mode = texture_mode;
- _current_width = _width;
-
- emit_changed();
-}
-
-Ref<Curve> CurveTexture::get_curve() const {
- return _curve;
-}
-
-void CurveTexture::set_texture_mode(TextureMode p_mode) {
- ERR_FAIL_COND(p_mode < TEXTURE_MODE_RGB || p_mode > TEXTURE_MODE_RED);
- if (texture_mode == p_mode) {
- return;
- }
- texture_mode = p_mode;
- _update();
-}
-CurveTexture::TextureMode CurveTexture::get_texture_mode() const {
- return texture_mode;
-}
-
-RID CurveTexture::get_rid() const {
- if (!_texture.is_valid()) {
- _texture = RS::get_singleton()->texture_2d_placeholder_create();
- }
- return _texture;
-}
-
-CurveTexture::CurveTexture() {}
-
-CurveTexture::~CurveTexture() {
- if (_texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(_texture);
- }
-}
-
-//////////////////
-
-void CurveXYZTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveXYZTexture::set_width);
-
- ClassDB::bind_method(D_METHOD("set_curve_x", "curve"), &CurveXYZTexture::set_curve_x);
- ClassDB::bind_method(D_METHOD("get_curve_x"), &CurveXYZTexture::get_curve_x);
-
- ClassDB::bind_method(D_METHOD("set_curve_y", "curve"), &CurveXYZTexture::set_curve_y);
- ClassDB::bind_method(D_METHOD("get_curve_y"), &CurveXYZTexture::get_curve_y);
-
- ClassDB::bind_method(D_METHOD("set_curve_z", "curve"), &CurveXYZTexture::set_curve_z);
- ClassDB::bind_method(D_METHOD("get_curve_z"), &CurveXYZTexture::get_curve_z);
-
- ClassDB::bind_method(D_METHOD("_update"), &CurveXYZTexture::_update);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,4096,suffix:px"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_x", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_x", "get_curve_x");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_y", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_y", "get_curve_y");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve_z", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve_z", "get_curve_z");
-}
-
-void CurveXYZTexture::set_width(int p_width) {
- ERR_FAIL_COND(p_width < 32 || p_width > 4096);
-
- if (_width == p_width) {
- return;
- }
-
- _width = p_width;
- _update();
-}
-
-int CurveXYZTexture::get_width() const {
- return _width;
-}
-
-void CurveXYZTexture::ensure_default_setup(float p_min, float p_max) {
- if (_curve_x.is_null()) {
- Ref<Curve> curve = Ref<Curve>(memnew(Curve));
- curve->add_point(Vector2(0, 1));
- curve->add_point(Vector2(1, 1));
- curve->set_min_value(p_min);
- curve->set_max_value(p_max);
- set_curve_x(curve);
- }
-
- if (_curve_y.is_null()) {
- Ref<Curve> curve = Ref<Curve>(memnew(Curve));
- curve->add_point(Vector2(0, 1));
- curve->add_point(Vector2(1, 1));
- curve->set_min_value(p_min);
- curve->set_max_value(p_max);
- set_curve_y(curve);
- }
-
- if (_curve_z.is_null()) {
- Ref<Curve> curve = Ref<Curve>(memnew(Curve));
- curve->add_point(Vector2(0, 1));
- curve->add_point(Vector2(1, 1));
- curve->set_min_value(p_min);
- curve->set_max_value(p_max);
- set_curve_z(curve);
- }
-}
-
-void CurveXYZTexture::set_curve_x(Ref<Curve> p_curve) {
- if (_curve_x != p_curve) {
- if (_curve_x.is_valid()) {
- _curve_x->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update));
- }
- _curve_x = p_curve;
- if (_curve_x.is_valid()) {
- _curve_x->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED);
- }
- _update();
- }
-}
-
-void CurveXYZTexture::set_curve_y(Ref<Curve> p_curve) {
- if (_curve_y != p_curve) {
- if (_curve_y.is_valid()) {
- _curve_y->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update));
- }
- _curve_y = p_curve;
- if (_curve_y.is_valid()) {
- _curve_y->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED);
- }
- _update();
- }
-}
-
-void CurveXYZTexture::set_curve_z(Ref<Curve> p_curve) {
- if (_curve_z != p_curve) {
- if (_curve_z.is_valid()) {
- _curve_z->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update));
- }
- _curve_z = p_curve;
- if (_curve_z.is_valid()) {
- _curve_z->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveXYZTexture::_update), CONNECT_REFERENCE_COUNTED);
- }
- _update();
- }
-}
-
-void CurveXYZTexture::_update() {
- Vector<uint8_t> data;
- data.resize(_width * sizeof(float) * 3);
-
- // The array is locked in that scope
- {
- uint8_t *wd8 = data.ptrw();
- float *wd = (float *)wd8;
-
- if (_curve_x.is_valid()) {
- Curve &curve_x = **_curve_x;
- for (int i = 0; i < _width; ++i) {
- float t = i / static_cast<float>(_width);
- wd[i * 3 + 0] = curve_x.sample_baked(t);
- }
-
- } else {
- for (int i = 0; i < _width; ++i) {
- wd[i * 3 + 0] = 0;
- }
- }
-
- if (_curve_y.is_valid()) {
- Curve &curve_y = **_curve_y;
- for (int i = 0; i < _width; ++i) {
- float t = i / static_cast<float>(_width);
- wd[i * 3 + 1] = curve_y.sample_baked(t);
- }
-
- } else {
- for (int i = 0; i < _width; ++i) {
- wd[i * 3 + 1] = 0;
- }
- }
-
- if (_curve_z.is_valid()) {
- Curve &curve_z = **_curve_z;
- for (int i = 0; i < _width; ++i) {
- float t = i / static_cast<float>(_width);
- wd[i * 3 + 2] = curve_z.sample_baked(t);
- }
-
- } else {
- for (int i = 0; i < _width; ++i) {
- wd[i * 3 + 2] = 0;
- }
- }
- }
-
- Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RGBF, data));
-
- if (_texture.is_valid()) {
- if (_current_width != _width) {
- RID new_texture = RS::get_singleton()->texture_2d_create(image);
- RS::get_singleton()->texture_replace(_texture, new_texture);
- } else {
- RS::get_singleton()->texture_2d_update(_texture, image);
- }
- } else {
- _texture = RS::get_singleton()->texture_2d_create(image);
- }
- _current_width = _width;
-
- emit_changed();
-}
-
-Ref<Curve> CurveXYZTexture::get_curve_x() const {
- return _curve_x;
-}
-
-Ref<Curve> CurveXYZTexture::get_curve_y() const {
- return _curve_y;
-}
-
-Ref<Curve> CurveXYZTexture::get_curve_z() const {
- return _curve_z;
-}
-
-RID CurveXYZTexture::get_rid() const {
- if (!_texture.is_valid()) {
- _texture = RS::get_singleton()->texture_2d_placeholder_create();
- }
- return _texture;
-}
-
-CurveXYZTexture::CurveXYZTexture() {}
-
-CurveXYZTexture::~CurveXYZTexture() {
- if (_texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(_texture);
- }
-}
-
-//////////////////
-
-GradientTexture1D::GradientTexture1D() {
- _queue_update();
-}
-
-GradientTexture1D::~GradientTexture1D() {
- if (texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(texture);
- }
-}
-
-void GradientTexture1D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture1D::set_gradient);
- ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture1D::get_gradient);
-
- ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture1D::set_width);
- // The `get_width()` method is already exposed by the parent class Texture2D.
-
- ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture1D::set_use_hdr);
- ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture1D::is_using_hdr);
-
- ClassDB::bind_method(D_METHOD("_update"), &GradientTexture1D::_update);
-
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,16384,suffix:px"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
-}
-
-void GradientTexture1D::set_gradient(Ref<Gradient> p_gradient) {
- if (p_gradient == gradient) {
- return;
- }
- if (gradient.is_valid()) {
- gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture1D::_update));
- }
- gradient = p_gradient;
- if (gradient.is_valid()) {
- gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture1D::_update));
- }
- _update();
- emit_changed();
-}
-
-Ref<Gradient> GradientTexture1D::get_gradient() const {
- return gradient;
-}
-
-void GradientTexture1D::_queue_update() {
- if (update_pending) {
- return;
- }
-
- update_pending = true;
- call_deferred(SNAME("_update"));
-}
-
-void GradientTexture1D::_update() {
- update_pending = false;
-
- if (gradient.is_null()) {
- return;
- }
-
- if (use_hdr) {
- // High dynamic range.
- Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBAF));
- Gradient &g = **gradient;
- // `create()` isn't available for non-uint8_t data, so fill in the data manually.
- for (int i = 0; i < width; i++) {
- float ofs = float(i) / (width - 1);
- image->set_pixel(i, 0, g.get_color_at_offset(ofs));
- }
-
- if (texture.is_valid()) {
- RID new_texture = RS::get_singleton()->texture_2d_create(image);
- RS::get_singleton()->texture_replace(texture, new_texture);
- } else {
- texture = RS::get_singleton()->texture_2d_create(image);
- }
- } else {
- // Low dynamic range. "Overbright" colors will be clamped.
- Vector<uint8_t> data;
- data.resize(width * 4);
- {
- uint8_t *wd8 = data.ptrw();
- Gradient &g = **gradient;
-
- for (int i = 0; i < width; i++) {
- float ofs = float(i) / (width - 1);
- Color color = g.get_color_at_offset(ofs);
-
- wd8[i * 4 + 0] = uint8_t(CLAMP(color.r * 255.0, 0, 255));
- wd8[i * 4 + 1] = uint8_t(CLAMP(color.g * 255.0, 0, 255));
- wd8[i * 4 + 2] = uint8_t(CLAMP(color.b * 255.0, 0, 255));
- wd8[i * 4 + 3] = uint8_t(CLAMP(color.a * 255.0, 0, 255));
- }
- }
-
- Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data));
-
- if (texture.is_valid()) {
- RID new_texture = RS::get_singleton()->texture_2d_create(image);
- RS::get_singleton()->texture_replace(texture, new_texture);
- } else {
- texture = RS::get_singleton()->texture_2d_create(image);
- }
- }
-
- emit_changed();
-}
-
-void GradientTexture1D::set_width(int p_width) {
- ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be within 1 to 16384 range.");
- width = p_width;
- _queue_update();
-}
-
-int GradientTexture1D::get_width() const {
- return width;
-}
-
-void GradientTexture1D::set_use_hdr(bool p_enabled) {
- if (p_enabled == use_hdr) {
- return;
- }
-
- use_hdr = p_enabled;
- _queue_update();
-}
-
-bool GradientTexture1D::is_using_hdr() const {
- return use_hdr;
-}
-
-Ref<Image> GradientTexture1D::get_image() const {
- if (!texture.is_valid()) {
- return Ref<Image>();
- }
- return RenderingServer::get_singleton()->texture_2d_get(texture);
-}
-
-//////////////////
-
-GradientTexture2D::GradientTexture2D() {
- _queue_update();
-}
-
-GradientTexture2D::~GradientTexture2D() {
- if (texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(texture);
- }
-}
-
-void GradientTexture2D::set_gradient(Ref<Gradient> p_gradient) {
- if (gradient == p_gradient) {
- return;
- }
- if (gradient.is_valid()) {
- gradient->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update));
- }
- gradient = p_gradient;
- if (gradient.is_valid()) {
- gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update));
- }
- _update();
- emit_changed();
-}
-
-Ref<Gradient> GradientTexture2D::get_gradient() const {
- return gradient;
-}
-
-void GradientTexture2D::_queue_update() {
- if (update_pending) {
- return;
- }
- update_pending = true;
- call_deferred(SNAME("_update"));
-}
-
-void GradientTexture2D::_update() {
- update_pending = false;
-
- if (gradient.is_null()) {
- return;
- }
- Ref<Image> image;
- image.instantiate();
-
- if (gradient->get_point_count() <= 1) { // No need to interpolate.
- image->initialize_data(width, height, false, (use_hdr) ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8);
- image->fill((gradient->get_point_count() == 1) ? gradient->get_color(0) : Color(0, 0, 0, 1));
- } else {
- if (use_hdr) {
- image->initialize_data(width, height, false, Image::FORMAT_RGBAF);
- Gradient &g = **gradient;
- // `create()` isn't available for non-uint8_t data, so fill in the data manually.
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- float ofs = _get_gradient_offset_at(x, y);
- image->set_pixel(x, y, g.get_color_at_offset(ofs));
- }
- }
- } else {
- Vector<uint8_t> data;
- data.resize(width * height * 4);
- {
- uint8_t *wd8 = data.ptrw();
- Gradient &g = **gradient;
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- float ofs = _get_gradient_offset_at(x, y);
- const Color &c = g.get_color_at_offset(ofs);
-
- wd8[(x + (y * width)) * 4 + 0] = uint8_t(CLAMP(c.r * 255.0, 0, 255));
- wd8[(x + (y * width)) * 4 + 1] = uint8_t(CLAMP(c.g * 255.0, 0, 255));
- wd8[(x + (y * width)) * 4 + 2] = uint8_t(CLAMP(c.b * 255.0, 0, 255));
- wd8[(x + (y * width)) * 4 + 3] = uint8_t(CLAMP(c.a * 255.0, 0, 255));
- }
- }
- }
- image->set_data(width, height, false, Image::FORMAT_RGBA8, data);
- }
- }
-
- if (texture.is_valid()) {
- RID new_texture = RS::get_singleton()->texture_2d_create(image);
- RS::get_singleton()->texture_replace(texture, new_texture);
- } else {
- texture = RS::get_singleton()->texture_2d_create(image);
- }
- emit_changed();
-}
-
-float GradientTexture2D::_get_gradient_offset_at(int x, int y) const {
- if (fill_to == fill_from) {
- return 0;
- }
- float ofs = 0;
- Vector2 pos;
- if (width > 1) {
- pos.x = static_cast<float>(x) / (width - 1);
- }
- if (height > 1) {
- pos.y = static_cast<float>(y) / (height - 1);
- }
- if (fill == Fill::FILL_LINEAR) {
- Vector2 segment[2];
- segment[0] = fill_from;
- segment[1] = fill_to;
- Vector2 closest = Geometry2D::get_closest_point_to_segment_uncapped(pos, &segment[0]);
- ofs = (closest - fill_from).length() / (fill_to - fill_from).length();
- if ((closest - fill_from).dot(fill_to - fill_from) < 0) {
- ofs *= -1;
- }
- } else if (fill == Fill::FILL_RADIAL) {
- ofs = (pos - fill_from).length() / (fill_to - fill_from).length();
- } else if (fill == Fill::FILL_SQUARE) {
- ofs = MAX(Math::abs(pos.x - fill_from.x), Math::abs(pos.y - fill_from.y)) / MAX(Math::abs(fill_to.x - fill_from.x), Math::abs(fill_to.y - fill_from.y));
- }
- if (repeat == Repeat::REPEAT_NONE) {
- ofs = CLAMP(ofs, 0.0, 1.0);
- } else if (repeat == Repeat::REPEAT) {
- ofs = Math::fmod(ofs, 1.0f);
- if (ofs < 0) {
- ofs = 1 + ofs;
- }
- } else if (repeat == Repeat::REPEAT_MIRROR) {
- ofs = Math::abs(ofs);
- ofs = Math::fmod(ofs, 2.0f);
- if (ofs > 1.0) {
- ofs = 2.0 - ofs;
- }
- }
- return ofs;
-}
-
-void GradientTexture2D::set_width(int p_width) {
- ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be within 1 to 16384 range.");
- width = p_width;
- _queue_update();
-}
-
-int GradientTexture2D::get_width() const {
- return width;
-}
-
-void GradientTexture2D::set_height(int p_height) {
- ERR_FAIL_COND_MSG(p_height <= 0 || p_height > 16384, "Texture dimensions have to be within 1 to 16384 range.");
- height = p_height;
- _queue_update();
-}
-int GradientTexture2D::get_height() const {
- return height;
-}
-
-void GradientTexture2D::set_use_hdr(bool p_enabled) {
- if (p_enabled == use_hdr) {
- return;
- }
-
- use_hdr = p_enabled;
- _queue_update();
-}
-
-bool GradientTexture2D::is_using_hdr() const {
- return use_hdr;
-}
-
-void GradientTexture2D::set_fill_from(Vector2 p_fill_from) {
- fill_from = p_fill_from;
- _queue_update();
-}
-
-Vector2 GradientTexture2D::get_fill_from() const {
- return fill_from;
-}
-
-void GradientTexture2D::set_fill_to(Vector2 p_fill_to) {
- fill_to = p_fill_to;
- _queue_update();
-}
-
-Vector2 GradientTexture2D::get_fill_to() const {
- return fill_to;
-}
-
-void GradientTexture2D::set_fill(Fill p_fill) {
- fill = p_fill;
- _queue_update();
-}
-
-GradientTexture2D::Fill GradientTexture2D::get_fill() const {
- return fill;
-}
-
-void GradientTexture2D::set_repeat(Repeat p_repeat) {
- repeat = p_repeat;
- _queue_update();
-}
-
-GradientTexture2D::Repeat GradientTexture2D::get_repeat() const {
- return repeat;
-}
-
-RID GradientTexture2D::get_rid() const {
- if (!texture.is_valid()) {
- texture = RS::get_singleton()->texture_2d_placeholder_create();
- }
- return texture;
-}
-
-Ref<Image> GradientTexture2D::get_image() const {
- if (!texture.is_valid()) {
- return Ref<Image>();
- }
- return RenderingServer::get_singleton()->texture_2d_get(texture);
-}
-
-void GradientTexture2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture2D::set_gradient);
- ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture2D::get_gradient);
-
- ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture2D::set_width);
- ClassDB::bind_method(D_METHOD("set_height", "height"), &GradientTexture2D::set_height);
-
- ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture2D::set_use_hdr);
- ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture2D::is_using_hdr);
-
- ClassDB::bind_method(D_METHOD("set_fill", "fill"), &GradientTexture2D::set_fill);
- ClassDB::bind_method(D_METHOD("get_fill"), &GradientTexture2D::get_fill);
- ClassDB::bind_method(D_METHOD("set_fill_from", "fill_from"), &GradientTexture2D::set_fill_from);
- ClassDB::bind_method(D_METHOD("get_fill_from"), &GradientTexture2D::get_fill_from);
- ClassDB::bind_method(D_METHOD("set_fill_to", "fill_to"), &GradientTexture2D::set_fill_to);
- ClassDB::bind_method(D_METHOD("get_fill_to"), &GradientTexture2D::get_fill_to);
-
- ClassDB::bind_method(D_METHOD("set_repeat", "repeat"), &GradientTexture2D::set_repeat);
- ClassDB::bind_method(D_METHOD("get_repeat"), &GradientTexture2D::get_repeat);
-
- ClassDB::bind_method(D_METHOD("_update"), &GradientTexture2D::_update);
-
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,or_greater,suffix:px"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,or_greater,suffix:px"), "set_height", "get_height");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
-
- ADD_GROUP("Fill", "fill_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "fill", PROPERTY_HINT_ENUM, "Linear,Radial,Square"), "set_fill", "get_fill");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_from"), "set_fill_from", "get_fill_from");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_to"), "set_fill_to", "get_fill_to");
-
- ADD_GROUP("Repeat", "repeat_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "repeat", PROPERTY_HINT_ENUM, "No Repeat,Repeat,Mirror Repeat"), "set_repeat", "get_repeat");
-
- BIND_ENUM_CONSTANT(FILL_LINEAR);
- BIND_ENUM_CONSTANT(FILL_RADIAL);
- BIND_ENUM_CONSTANT(FILL_SQUARE);
-
- BIND_ENUM_CONSTANT(REPEAT_NONE);
- BIND_ENUM_CONSTANT(REPEAT);
- BIND_ENUM_CONSTANT(REPEAT_MIRROR);
-}
-
-//////////////////////////////////////
-
-void AnimatedTexture::_update_proxy() {
- RWLockRead r(rw_lock);
-
- float delta;
- if (prev_ticks == 0) {
- delta = 0;
- prev_ticks = OS::get_singleton()->get_ticks_usec();
- } else {
- uint64_t ticks = OS::get_singleton()->get_ticks_usec();
- delta = float(double(ticks - prev_ticks) / 1000000.0);
- prev_ticks = ticks;
- }
-
- time += delta;
-
- float speed = speed_scale == 0 ? 0 : abs(1.0 / speed_scale);
-
- int iter_max = frame_count;
- while (iter_max && !pause) {
- float frame_limit = frames[current_frame].duration * speed;
-
- if (time > frame_limit) {
- if (speed_scale > 0.0) {
- current_frame++;
- } else {
- current_frame--;
- }
- if (current_frame >= frame_count) {
- if (one_shot) {
- current_frame = frame_count - 1;
- } else {
- current_frame = 0;
- }
- } else if (current_frame < 0) {
- if (one_shot) {
- current_frame = 0;
- } else {
- current_frame = frame_count - 1;
- }
- }
- time -= frame_limit;
-
- } else {
- break;
- }
- iter_max--;
- }
-
- if (frames[current_frame].texture.is_valid()) {
- RenderingServer::get_singleton()->texture_proxy_update(proxy, frames[current_frame].texture->get_rid());
- }
-}
-
-void AnimatedTexture::set_frames(int p_frames) {
- ERR_FAIL_COND(p_frames < 1 || p_frames > MAX_FRAMES);
-
- RWLockWrite r(rw_lock);
-
- frame_count = p_frames;
-}
-
-int AnimatedTexture::get_frames() const {
- return frame_count;
-}
-
-void AnimatedTexture::set_current_frame(int p_frame) {
- ERR_FAIL_COND(p_frame < 0 || p_frame >= frame_count);
-
- RWLockWrite r(rw_lock);
-
- current_frame = p_frame;
- time = 0;
-}
-
-int AnimatedTexture::get_current_frame() const {
- return current_frame;
-}
-
-void AnimatedTexture::set_pause(bool p_pause) {
- RWLockWrite r(rw_lock);
- pause = p_pause;
-}
-
-bool AnimatedTexture::get_pause() const {
- return pause;
-}
-
-void AnimatedTexture::set_one_shot(bool p_one_shot) {
- RWLockWrite r(rw_lock);
- one_shot = p_one_shot;
-}
-
-bool AnimatedTexture::get_one_shot() const {
- return one_shot;
-}
-
-void AnimatedTexture::set_frame_texture(int p_frame, const Ref<Texture2D> &p_texture) {
- ERR_FAIL_COND(p_texture == this);
- ERR_FAIL_INDEX(p_frame, MAX_FRAMES);
-
- RWLockWrite w(rw_lock);
-
- frames[p_frame].texture = p_texture;
-}
-
-Ref<Texture2D> AnimatedTexture::get_frame_texture(int p_frame) const {
- ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, Ref<Texture2D>());
-
- RWLockRead r(rw_lock);
-
- return frames[p_frame].texture;
-}
-
-void AnimatedTexture::set_frame_duration(int p_frame, float p_duration) {
- ERR_FAIL_INDEX(p_frame, MAX_FRAMES);
-
- RWLockWrite r(rw_lock);
-
- frames[p_frame].duration = p_duration;
-}
-
-float AnimatedTexture::get_frame_duration(int p_frame) const {
- ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, 0);
-
- RWLockRead r(rw_lock);
-
- return frames[p_frame].duration;
-}
-
-void AnimatedTexture::set_speed_scale(float p_scale) {
- ERR_FAIL_COND(p_scale < -1000 || p_scale >= 1000);
-
- RWLockWrite r(rw_lock);
-
- speed_scale = p_scale;
-}
-
-float AnimatedTexture::get_speed_scale() const {
- return speed_scale;
-}
-
-int AnimatedTexture::get_width() const {
- RWLockRead r(rw_lock);
-
- if (!frames[current_frame].texture.is_valid()) {
- return 1;
- }
-
- return frames[current_frame].texture->get_width();
-}
-
-int AnimatedTexture::get_height() const {
- RWLockRead r(rw_lock);
-
- if (!frames[current_frame].texture.is_valid()) {
- return 1;
- }
-
- return frames[current_frame].texture->get_height();
-}
-
-RID AnimatedTexture::get_rid() const {
- return proxy;
-}
-
-bool AnimatedTexture::has_alpha() const {
- RWLockRead r(rw_lock);
-
- if (!frames[current_frame].texture.is_valid()) {
- return false;
- }
-
- return frames[current_frame].texture->has_alpha();
-}
-
-Ref<Image> AnimatedTexture::get_image() const {
- RWLockRead r(rw_lock);
-
- if (!frames[current_frame].texture.is_valid()) {
- return Ref<Image>();
- }
-
- return frames[current_frame].texture->get_image();
-}
-
-bool AnimatedTexture::is_pixel_opaque(int p_x, int p_y) const {
- RWLockRead r(rw_lock);
-
- if (frames[current_frame].texture.is_valid()) {
- return frames[current_frame].texture->is_pixel_opaque(p_x, p_y);
- }
- return true;
-}
-
-void AnimatedTexture::_validate_property(PropertyInfo &p_property) const {
- String prop = p_property.name;
- if (prop.begins_with("frame_")) {
- int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int();
- if (frame >= frame_count) {
- p_property.usage = PROPERTY_USAGE_NONE;
- }
- }
-}
-
-void AnimatedTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_frames", "frames"), &AnimatedTexture::set_frames);
- ClassDB::bind_method(D_METHOD("get_frames"), &AnimatedTexture::get_frames);
-
- ClassDB::bind_method(D_METHOD("set_current_frame", "frame"), &AnimatedTexture::set_current_frame);
- ClassDB::bind_method(D_METHOD("get_current_frame"), &AnimatedTexture::get_current_frame);
-
- ClassDB::bind_method(D_METHOD("set_pause", "pause"), &AnimatedTexture::set_pause);
- ClassDB::bind_method(D_METHOD("get_pause"), &AnimatedTexture::get_pause);
-
- ClassDB::bind_method(D_METHOD("set_one_shot", "one_shot"), &AnimatedTexture::set_one_shot);
- ClassDB::bind_method(D_METHOD("get_one_shot"), &AnimatedTexture::get_one_shot);
-
- ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &AnimatedTexture::set_speed_scale);
- ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedTexture::get_speed_scale);
-
- ClassDB::bind_method(D_METHOD("set_frame_texture", "frame", "texture"), &AnimatedTexture::set_frame_texture);
- ClassDB::bind_method(D_METHOD("get_frame_texture", "frame"), &AnimatedTexture::get_frame_texture);
-
- ClassDB::bind_method(D_METHOD("set_frame_duration", "frame", "duration"), &AnimatedTexture::set_frame_duration);
- ClassDB::bind_method(D_METHOD("get_frame_duration", "frame"), &AnimatedTexture::get_frame_duration);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "current_frame", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_frame", "get_current_frame");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pause"), "set_pause", "get_pause");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-60,60,0.1,or_less,or_greater"), "set_speed_scale", "get_speed_scale");
-
- for (int i = 0; i < MAX_FRAMES; i++) {
- ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_texture", "get_frame_texture", i);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "frame_" + itos(i) + "/duration", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_frame_duration", "get_frame_duration", i);
- }
-
- BIND_CONSTANT(MAX_FRAMES);
-}
-
-AnimatedTexture::AnimatedTexture() {
- //proxy = RS::get_singleton()->texture_create();
- proxy_ph = RS::get_singleton()->texture_2d_placeholder_create();
- proxy = RS::get_singleton()->texture_proxy_create(proxy_ph);
-
- RenderingServer::get_singleton()->texture_set_force_redraw_if_visible(proxy, true);
- RenderingServer::get_singleton()->connect("frame_pre_draw", callable_mp(this, &AnimatedTexture::_update_proxy));
-}
-
-AnimatedTexture::~AnimatedTexture() {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(proxy);
- RS::get_singleton()->free(proxy_ph);
-}
-
-///////////////////////////////
-
Image::Format TextureLayered::get_format() const {
Image::Format ret = Image::FORMAT_MAX;
GDVIRTUAL_REQUIRED_CALL(_get_format, ret);
@@ -2896,654 +259,3 @@ void TextureLayered::_bind_methods() {
GDVIRTUAL_BIND(_has_mipmaps);
GDVIRTUAL_BIND(_get_layer_data, "layer_index");
}
-
-///////////////////////////////
-Image::Format ImageTextureLayered::get_format() const {
- return format;
-}
-
-int ImageTextureLayered::get_width() const {
- return width;
-}
-
-int ImageTextureLayered::get_height() const {
- return height;
-}
-
-int ImageTextureLayered::get_layers() const {
- return layers;
-}
-
-bool ImageTextureLayered::has_mipmaps() const {
- return mipmaps;
-}
-
-ImageTextureLayered::LayeredType ImageTextureLayered::get_layered_type() const {
- return layered_type;
-}
-
-Error ImageTextureLayered::_create_from_images(const TypedArray<Image> &p_images) {
- Vector<Ref<Image>> images;
- for (int i = 0; i < p_images.size(); i++) {
- Ref<Image> img = p_images[i];
- ERR_FAIL_COND_V(img.is_null(), ERR_INVALID_PARAMETER);
- images.push_back(img);
- }
-
- return create_from_images(images);
-}
-
-TypedArray<Image> ImageTextureLayered::_get_images() const {
- TypedArray<Image> images;
- for (int i = 0; i < layers; i++) {
- images.push_back(get_layer_data(i));
- }
- return images;
-}
-
-void ImageTextureLayered::_set_images(const TypedArray<Image> &p_images) {
- ERR_FAIL_COND(_create_from_images(p_images) != OK);
-}
-
-Error ImageTextureLayered::create_from_images(Vector<Ref<Image>> p_images) {
- int new_layers = p_images.size();
- ERR_FAIL_COND_V(new_layers == 0, ERR_INVALID_PARAMETER);
- if (layered_type == LAYERED_TYPE_CUBEMAP) {
- ERR_FAIL_COND_V_MSG(new_layers != 6, ERR_INVALID_PARAMETER,
- "Cubemaps require exactly 6 layers");
- } else if (layered_type == LAYERED_TYPE_CUBEMAP_ARRAY) {
- ERR_FAIL_COND_V_MSG((new_layers % 6) != 0, ERR_INVALID_PARAMETER,
- "Cubemap array layers must be a multiple of 6");
- }
-
- ERR_FAIL_COND_V(p_images[0].is_null() || p_images[0]->is_empty(), ERR_INVALID_PARAMETER);
-
- Image::Format new_format = p_images[0]->get_format();
- int new_width = p_images[0]->get_width();
- int new_height = p_images[0]->get_height();
- bool new_mipmaps = p_images[0]->has_mipmaps();
-
- for (int i = 1; i < p_images.size(); i++) {
- ERR_FAIL_COND_V_MSG(p_images[i]->get_format() != new_format, ERR_INVALID_PARAMETER,
- "All images must share the same format");
- ERR_FAIL_COND_V_MSG(p_images[i]->get_width() != new_width || p_images[i]->get_height() != new_height, ERR_INVALID_PARAMETER,
- "All images must share the same dimensions");
- ERR_FAIL_COND_V_MSG(p_images[i]->has_mipmaps() != new_mipmaps, ERR_INVALID_PARAMETER,
- "All images must share the usage of mipmaps");
- }
-
- if (texture.is_valid()) {
- RID new_texture = RS::get_singleton()->texture_2d_layered_create(p_images, RS::TextureLayeredType(layered_type));
- ERR_FAIL_COND_V(!new_texture.is_valid(), ERR_CANT_CREATE);
- RS::get_singleton()->texture_replace(texture, new_texture);
- } else {
- texture = RS::get_singleton()->texture_2d_layered_create(p_images, RS::TextureLayeredType(layered_type));
- ERR_FAIL_COND_V(!texture.is_valid(), ERR_CANT_CREATE);
- }
-
- format = new_format;
- width = new_width;
- height = new_height;
- layers = new_layers;
- mipmaps = new_mipmaps;
- return OK;
-}
-
-void ImageTextureLayered::update_layer(const Ref<Image> &p_image, int p_layer) {
- ERR_FAIL_COND_MSG(texture.is_null(), "Texture is not initialized.");
- ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image.");
- ERR_FAIL_COND_MSG(p_image->get_format() != format, "Image format must match texture's image format.");
- ERR_FAIL_COND_MSG(p_image->get_width() != width || p_image->get_height() != height, "Image size must match texture's image size.");
- ERR_FAIL_COND_MSG(p_image->has_mipmaps() != mipmaps, "Image mipmap configuration must match texture's image mipmap configuration.");
- ERR_FAIL_INDEX_MSG(p_layer, layers, "Layer index is out of bounds.");
- RS::get_singleton()->texture_2d_update(texture, p_image, p_layer);
-}
-
-Ref<Image> ImageTextureLayered::get_layer_data(int p_layer) const {
- ERR_FAIL_INDEX_V(p_layer, layers, Ref<Image>());
- return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
-}
-
-RID ImageTextureLayered::get_rid() const {
- if (texture.is_null()) {
- texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
- }
- return texture;
-}
-
-void ImageTextureLayered::set_path(const String &p_path, bool p_take_over) {
- if (texture.is_valid()) {
- RS::get_singleton()->texture_set_path(texture, p_path);
- }
-
- Resource::set_path(p_path, p_take_over);
-}
-
-void ImageTextureLayered::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_from_images", "images"), &ImageTextureLayered::_create_from_images);
- ClassDB::bind_method(D_METHOD("update_layer", "image", "layer"), &ImageTextureLayered::update_layer);
-
- ClassDB::bind_method(D_METHOD("_get_images"), &ImageTextureLayered::_get_images);
- ClassDB::bind_method(D_METHOD("_set_images", "images"), &ImageTextureLayered::_set_images);
-
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_images", PROPERTY_HINT_ARRAY_TYPE, "Image", PROPERTY_USAGE_INTERNAL), "_set_images", "_get_images");
-}
-
-ImageTextureLayered::ImageTextureLayered(LayeredType p_layered_type) {
- layered_type = p_layered_type;
-}
-
-ImageTextureLayered::~ImageTextureLayered() {
- if (texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(texture);
- }
-}
-
-void Texture2DArray::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_placeholder"), &Texture2DArray::create_placeholder);
-}
-
-Ref<Resource> Texture2DArray::create_placeholder() const {
- Ref<PlaceholderTexture2DArray> placeholder;
- placeholder.instantiate();
- placeholder->set_size(Size2i(get_width(), get_height()));
- placeholder->set_layers(get_layers());
- return placeholder;
-}
-
-void Cubemap::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_placeholder"), &Cubemap::create_placeholder);
-}
-
-Ref<Resource> Cubemap::create_placeholder() const {
- Ref<PlaceholderCubemap> placeholder;
- placeholder.instantiate();
- placeholder->set_size(Size2i(get_width(), get_height()));
- placeholder->set_layers(get_layers());
- return placeholder;
-}
-
-void CubemapArray::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_placeholder"), &CubemapArray::create_placeholder);
-}
-
-Ref<Resource> CubemapArray::create_placeholder() const {
- Ref<PlaceholderCubemapArray> placeholder;
- placeholder.instantiate();
- placeholder->set_size(Size2i(get_width(), get_height()));
- placeholder->set_layers(get_layers());
- return placeholder;
-}
-
-///////////////////////////////////////////
-
-void CompressedTextureLayered::set_path(const String &p_path, bool p_take_over) {
- if (texture.is_valid()) {
- RenderingServer::get_singleton()->texture_set_path(texture, p_path);
- }
-
- Resource::set_path(p_path, p_take_over);
-}
-
-Image::Format CompressedTextureLayered::get_format() const {
- return format;
-}
-
-Error CompressedTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) {
- ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER);
-
- Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
-
- uint8_t header[4];
- f->get_buffer(header, 4);
- if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != 'L') {
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture layered file is corrupt (Bad header).");
- }
-
- uint32_t version = f->get_32();
-
- if (version > FORMAT_VERSION) {
- ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new.");
- }
-
- uint32_t layer_count = f->get_32(); //layer count
- uint32_t type = f->get_32(); //layer count
- ERR_FAIL_COND_V((int)type != layered_type, ERR_INVALID_DATA);
-
- uint32_t df = f->get_32(); //data format
- mipmap_limit = int(f->get_32());
- //reserved
- f->get_32();
- f->get_32();
- f->get_32();
-
- if (!(df & FORMAT_BIT_STREAM)) {
- p_size_limit = 0;
- }
-
- images.resize(layer_count);
-
- for (uint32_t i = 0; i < layer_count; i++) {
- Ref<Image> image = CompressedTexture2D::load_image_from_file(f, p_size_limit);
- ERR_FAIL_COND_V(image.is_null() || image->is_empty(), ERR_CANT_OPEN);
- images.write[i] = image;
- }
-
- return OK;
-}
-
-Error CompressedTextureLayered::load(const String &p_path) {
- Vector<Ref<Image>> images;
-
- int mipmap_limit;
-
- Error err = _load_data(p_path, images, mipmap_limit);
- if (err) {
- return err;
- }
-
- if (texture.is_valid()) {
- RID new_texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));
- RS::get_singleton()->texture_replace(texture, new_texture);
- } else {
- texture = RS::get_singleton()->texture_2d_layered_create(images, RS::TextureLayeredType(layered_type));
- }
-
- w = images[0]->get_width();
- h = images[0]->get_height();
- mipmaps = images[0]->has_mipmaps();
- format = images[0]->get_format();
- layers = images.size();
-
- path_to_file = p_path;
-
- if (get_path().is_empty()) {
- //temporarily set path if no path set for resource, helps find errors
- RenderingServer::get_singleton()->texture_set_path(texture, p_path);
- }
-
- notify_property_list_changed();
- emit_changed();
- return OK;
-}
-
-String CompressedTextureLayered::get_load_path() const {
- return path_to_file;
-}
-
-int CompressedTextureLayered::get_width() const {
- return w;
-}
-
-int CompressedTextureLayered::get_height() const {
- return h;
-}
-
-int CompressedTextureLayered::get_layers() const {
- return layers;
-}
-
-bool CompressedTextureLayered::has_mipmaps() const {
- return mipmaps;
-}
-
-TextureLayered::LayeredType CompressedTextureLayered::get_layered_type() const {
- return layered_type;
-}
-
-RID CompressedTextureLayered::get_rid() const {
- if (!texture.is_valid()) {
- texture = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
- }
- return texture;
-}
-
-Ref<Image> CompressedTextureLayered::get_layer_data(int p_layer) const {
- if (texture.is_valid()) {
- return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
- } else {
- return Ref<Image>();
- }
-}
-
-void CompressedTextureLayered::reload_from_file() {
- String path = get_path();
- if (!path.is_resource_file()) {
- return;
- }
-
- path = ResourceLoader::path_remap(path); //remap for translation
- path = ResourceLoader::import_remap(path); //remap for import
- if (!path.is_resource_file()) {
- return;
- }
-
- load(path);
-}
-
-void CompressedTextureLayered::_validate_property(PropertyInfo &p_property) const {
-}
-
-void CompressedTextureLayered::_bind_methods() {
- ClassDB::bind_method(D_METHOD("load", "path"), &CompressedTextureLayered::load);
- ClassDB::bind_method(D_METHOD("get_load_path"), &CompressedTextureLayered::get_load_path);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "load_path", PROPERTY_HINT_FILE, "*.ctex"), "load", "get_load_path");
-}
-
-CompressedTextureLayered::CompressedTextureLayered(LayeredType p_type) {
- layered_type = p_type;
-}
-
-CompressedTextureLayered::~CompressedTextureLayered() {
- if (texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(texture);
- }
-}
-
-/////////////////////////////////////////////////
-
-Ref<Resource> ResourceFormatLoaderCompressedTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- Ref<CompressedTextureLayered> ct;
- if (p_path.get_extension().to_lower() == "ctexarray") {
- Ref<CompressedTexture2DArray> c;
- c.instantiate();
- ct = c;
- } else if (p_path.get_extension().to_lower() == "ccube") {
- Ref<CompressedCubemap> c;
- c.instantiate();
- ct = c;
- } else if (p_path.get_extension().to_lower() == "ccubearray") {
- Ref<CompressedCubemapArray> c;
- c.instantiate();
- ct = c;
- } else {
- if (r_error) {
- *r_error = ERR_FILE_UNRECOGNIZED;
- }
- return Ref<Resource>();
- }
- Error err = ct->load(p_path);
- if (r_error) {
- *r_error = err;
- }
- if (err != OK) {
- return Ref<Resource>();
- }
-
- return ct;
-}
-
-void ResourceFormatLoaderCompressedTextureLayered::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("ctexarray");
- p_extensions->push_back("ccube");
- p_extensions->push_back("ccubearray");
-}
-
-bool ResourceFormatLoaderCompressedTextureLayered::handles_type(const String &p_type) const {
- return p_type == "CompressedTexture2DArray" || p_type == "CompressedCubemap" || p_type == "CompressedCubemapArray";
-}
-
-String ResourceFormatLoaderCompressedTextureLayered::get_resource_type(const String &p_path) const {
- if (p_path.get_extension().to_lower() == "ctexarray") {
- return "CompressedTexture2DArray";
- }
- if (p_path.get_extension().to_lower() == "ccube") {
- return "CompressedCubemap";
- }
- if (p_path.get_extension().to_lower() == "ccubearray") {
- return "CompressedCubemapArray";
- }
- return "";
-}
-
-///////////////////////////////
-
-void CameraTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_camera_feed_id", "feed_id"), &CameraTexture::set_camera_feed_id);
- ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &CameraTexture::get_camera_feed_id);
-
- ClassDB::bind_method(D_METHOD("set_which_feed", "which_feed"), &CameraTexture::set_which_feed);
- ClassDB::bind_method(D_METHOD("get_which_feed"), &CameraTexture::get_which_feed);
-
- ClassDB::bind_method(D_METHOD("set_camera_active", "active"), &CameraTexture::set_camera_active);
- ClassDB::bind_method(D_METHOD("get_camera_active"), &CameraTexture::get_camera_active);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "camera_feed_id"), "set_camera_feed_id", "get_camera_feed_id");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "which_feed"), "set_which_feed", "get_which_feed");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "camera_is_active"), "set_camera_active", "get_camera_active");
-}
-
-int CameraTexture::get_width() const {
- Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
- if (feed.is_valid()) {
- return feed->get_base_width();
- } else {
- return 0;
- }
-}
-
-int CameraTexture::get_height() const {
- Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
- if (feed.is_valid()) {
- return feed->get_base_height();
- } else {
- return 0;
- }
-}
-
-bool CameraTexture::has_alpha() const {
- return false;
-}
-
-RID CameraTexture::get_rid() const {
- Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
- if (feed.is_valid()) {
- return feed->get_texture(which_feed);
- } else {
- if (_texture.is_null()) {
- _texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
- }
- return _texture;
- }
-}
-
-Ref<Image> CameraTexture::get_image() const {
- // not (yet) supported
- return Ref<Image>();
-}
-
-void CameraTexture::set_camera_feed_id(int p_new_id) {
- camera_feed_id = p_new_id;
- notify_property_list_changed();
-}
-
-int CameraTexture::get_camera_feed_id() const {
- return camera_feed_id;
-}
-
-void CameraTexture::set_which_feed(CameraServer::FeedImage p_which) {
- which_feed = p_which;
- notify_property_list_changed();
-}
-
-CameraServer::FeedImage CameraTexture::get_which_feed() const {
- return which_feed;
-}
-
-void CameraTexture::set_camera_active(bool p_active) {
- Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
- if (feed.is_valid()) {
- feed->set_active(p_active);
- notify_property_list_changed();
- }
-}
-
-bool CameraTexture::get_camera_active() const {
- Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
- if (feed.is_valid()) {
- return feed->is_active();
- } else {
- return false;
- }
-}
-
-CameraTexture::CameraTexture() {}
-
-CameraTexture::~CameraTexture() {
- if (_texture.is_valid()) {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RenderingServer::get_singleton()->free(_texture);
- }
-}
-
-///////////////////////////
-
-void PlaceholderTexture2D::set_size(Size2 p_size) {
- size = p_size;
-}
-
-int PlaceholderTexture2D::get_width() const {
- return size.width;
-}
-
-int PlaceholderTexture2D::get_height() const {
- return size.height;
-}
-
-bool PlaceholderTexture2D::has_alpha() const {
- return false;
-}
-
-Ref<Image> PlaceholderTexture2D::get_image() const {
- return Ref<Image>();
-}
-
-RID PlaceholderTexture2D::get_rid() const {
- return rid;
-}
-
-void PlaceholderTexture2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture2D::set_size);
-
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
-}
-
-PlaceholderTexture2D::PlaceholderTexture2D() {
- rid = RS::get_singleton()->texture_2d_placeholder_create();
-}
-
-PlaceholderTexture2D::~PlaceholderTexture2D() {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(rid);
-}
-
-///////////////////////////////////////////////
-
-void PlaceholderTexture3D::set_size(const Vector3i &p_size) {
- size = p_size;
-}
-
-Vector3i PlaceholderTexture3D::get_size() const {
- return size;
-}
-
-Image::Format PlaceholderTexture3D::get_format() const {
- return Image::FORMAT_RGB8;
-}
-
-int PlaceholderTexture3D::get_width() const {
- return size.x;
-}
-
-int PlaceholderTexture3D::get_height() const {
- return size.y;
-}
-
-int PlaceholderTexture3D::get_depth() const {
- return size.z;
-}
-
-bool PlaceholderTexture3D::has_mipmaps() const {
- return false;
-}
-
-Vector<Ref<Image>> PlaceholderTexture3D::get_data() const {
- return Vector<Ref<Image>>();
-}
-
-void PlaceholderTexture3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture3D::set_size);
- ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTexture3D::get_size);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
-}
-
-PlaceholderTexture3D::PlaceholderTexture3D() {
- rid = RS::get_singleton()->texture_3d_placeholder_create();
-}
-PlaceholderTexture3D::~PlaceholderTexture3D() {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(rid);
-}
-
-/////////////////////////////////////////////////
-
-void PlaceholderTextureLayered::set_size(const Size2i &p_size) {
- size = p_size;
-}
-
-Size2i PlaceholderTextureLayered::get_size() const {
- return size;
-}
-
-void PlaceholderTextureLayered::set_layers(int p_layers) {
- layers = p_layers;
-}
-
-Image::Format PlaceholderTextureLayered::get_format() const {
- return Image::FORMAT_RGB8;
-}
-
-TextureLayered::LayeredType PlaceholderTextureLayered::get_layered_type() const {
- return layered_type;
-}
-
-int PlaceholderTextureLayered::get_width() const {
- return size.x;
-}
-
-int PlaceholderTextureLayered::get_height() const {
- return size.y;
-}
-
-int PlaceholderTextureLayered::get_layers() const {
- return layers;
-}
-
-bool PlaceholderTextureLayered::has_mipmaps() const {
- return false;
-}
-
-Ref<Image> PlaceholderTextureLayered::get_layer_data(int p_layer) const {
- return Ref<Image>();
-}
-
-void PlaceholderTextureLayered::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTextureLayered::set_size);
- ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTextureLayered::get_size);
- ClassDB::bind_method(D_METHOD("set_layers", "layers"), &PlaceholderTextureLayered::set_layers);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_RANGE, "1,4096"), "set_layers", "get_layers");
-}
-
-PlaceholderTextureLayered::PlaceholderTextureLayered(LayeredType p_type) {
- layered_type = p_type;
- rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
-}
-PlaceholderTextureLayered::~PlaceholderTextureLayered() {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(rid);
-}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 6b5e87ae21..e7840804bf 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -87,297 +87,6 @@ public:
Texture2D();
};
-class BitMap;
-
-class ImageTexture : public Texture2D {
- GDCLASS(ImageTexture, Texture2D);
- RES_BASE_EXTENSION("tex");
-
- mutable RID texture;
- Image::Format format = Image::FORMAT_L8;
- bool mipmaps = false;
- int w = 0;
- int h = 0;
- Size2 size_override;
- mutable Ref<BitMap> alpha_cache;
- bool image_stored = false;
-
-protected:
- virtual void reload_from_file() override;
-
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
- static void _bind_methods();
-
-public:
- void set_image(const Ref<Image> &p_image);
- static Ref<ImageTexture> create_from_image(const Ref<Image> &p_image);
-
- Image::Format get_format() const;
-
- void update(const Ref<Image> &p_image);
- Ref<Image> get_image() const override;
-
- int get_width() const override;
- int get_height() const override;
-
- virtual RID get_rid() const override;
-
- bool has_alpha() const override;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
-
- bool is_pixel_opaque(int p_x, int p_y) const override;
-
- void set_size_override(const Size2i &p_size);
-
- virtual void set_path(const String &p_path, bool p_take_over = false) override;
-
- ImageTexture();
- ~ImageTexture();
-};
-
-class PortableCompressedTexture2D : public Texture2D {
- GDCLASS(PortableCompressedTexture2D, Texture2D);
-
-public:
- enum CompressionMode {
- COMPRESSION_MODE_LOSSLESS,
- COMPRESSION_MODE_LOSSY,
- COMPRESSION_MODE_BASIS_UNIVERSAL,
- COMPRESSION_MODE_S3TC,
- COMPRESSION_MODE_ETC2,
- COMPRESSION_MODE_BPTC,
- };
-
-private:
- CompressionMode compression_mode = COMPRESSION_MODE_LOSSLESS;
- static bool keep_all_compressed_buffers;
- bool keep_compressed_buffer = false;
- Vector<uint8_t> compressed_buffer;
- Size2 size;
- Size2 size_override;
- bool mipmaps = false;
- Image::Format format = Image::FORMAT_L8;
-
- mutable RID texture;
- mutable Ref<BitMap> alpha_cache;
-
- bool image_stored = false;
-
-protected:
- Vector<uint8_t> _get_data() const;
- void _set_data(const Vector<uint8_t> &p_data);
-
- static void _bind_methods();
-
-public:
- CompressionMode get_compression_mode() const;
- void create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map = false, float p_lossy_quality = 0.8);
-
- Image::Format get_format() const;
-
- void update(const Ref<Image> &p_image);
- Ref<Image> get_image() const override;
-
- int get_width() const override;
- int get_height() const override;
-
- virtual RID get_rid() const override;
-
- bool has_alpha() const override;
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
-
- bool is_pixel_opaque(int p_x, int p_y) const override;
-
- virtual void set_path(const String &p_path, bool p_take_over = false) override;
-
- void set_size_override(const Size2 &p_size);
- Size2 get_size_override() const;
-
- void set_keep_compressed_buffer(bool p_keep);
- bool is_keeping_compressed_buffer() const;
-
- static void set_keep_all_compressed_buffers(bool p_keep);
- static bool is_keeping_all_compressed_buffers();
-
- PortableCompressedTexture2D();
- ~PortableCompressedTexture2D();
-};
-
-VARIANT_ENUM_CAST(PortableCompressedTexture2D::CompressionMode)
-
-class CompressedTexture2D : public Texture2D {
- GDCLASS(CompressedTexture2D, Texture2D);
-
-public:
- enum DataFormat {
- DATA_FORMAT_IMAGE,
- DATA_FORMAT_PNG,
- DATA_FORMAT_WEBP,
- DATA_FORMAT_BASIS_UNIVERSAL,
- };
-
- enum {
- FORMAT_VERSION = 1
- };
-
- enum FormatBits {
- FORMAT_BIT_STREAM = 1 << 22,
- FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
- FORMAT_BIT_DETECT_3D = 1 << 24,
- //FORMAT_BIT_DETECT_SRGB = 1 << 25,
- FORMAT_BIT_DETECT_NORMAL = 1 << 26,
- FORMAT_BIT_DETECT_ROUGNESS = 1 << 27,
- };
-
-private:
- Error _load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit = 0);
- String path_to_file;
- mutable RID texture;
- Image::Format format = Image::FORMAT_L8;
- int w = 0;
- int h = 0;
- mutable Ref<BitMap> alpha_cache;
-
- virtual void reload_from_file() override;
-
- static void _requested_3d(void *p_ud);
- static void _requested_roughness(void *p_ud, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel);
- static void _requested_normal(void *p_ud);
-
-protected:
- static void _bind_methods();
- void _validate_property(PropertyInfo &p_property) const;
-
-public:
- static Ref<Image> load_image_from_file(Ref<FileAccess> p_file, int p_size_limit);
-
- typedef void (*TextureFormatRequestCallback)(const Ref<CompressedTexture2D> &);
- typedef void (*TextureFormatRoughnessRequestCallback)(const Ref<CompressedTexture2D> &, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel);
-
- static TextureFormatRequestCallback request_3d_callback;
- static TextureFormatRoughnessRequestCallback request_roughness_callback;
- static TextureFormatRequestCallback request_normal_callback;
-
- Image::Format get_format() const;
- Error load(const String &p_path);
- String get_load_path() const;
-
- int get_width() const override;
- int get_height() const override;
- virtual RID get_rid() const override;
-
- virtual void set_path(const String &p_path, bool p_take_over) override;
-
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
-
- virtual bool has_alpha() const override;
- bool is_pixel_opaque(int p_x, int p_y) const override;
-
- virtual Ref<Image> get_image() const override;
-
- CompressedTexture2D();
- ~CompressedTexture2D();
-};
-
-class ResourceFormatLoaderCompressedTexture2D : public ResourceFormatLoader {
-public:
- virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
-};
-
-class AtlasTexture : public Texture2D {
- GDCLASS(AtlasTexture, Texture2D);
- RES_BASE_EXTENSION("atlastex");
-
-protected:
- Ref<Texture2D> atlas;
- Rect2 region;
- Rect2 margin;
- bool filter_clip = false;
-
- static void _bind_methods();
-
-public:
- virtual int get_width() const override;
- virtual int get_height() const override;
- virtual RID get_rid() const override;
-
- virtual bool has_alpha() const override;
-
- void set_atlas(const Ref<Texture2D> &p_atlas);
- Ref<Texture2D> get_atlas() const;
-
- void set_region(const Rect2 &p_region);
- Rect2 get_region() const;
-
- void set_margin(const Rect2 &p_margin);
- Rect2 get_margin() const;
-
- void set_filter_clip(const bool p_enable);
- bool has_filter_clip() const;
-
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
- virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const override;
-
- bool is_pixel_opaque(int p_x, int p_y) const override;
-
- virtual Ref<Image> get_image() const override;
-
- AtlasTexture();
-};
-
-class Mesh;
-
-class MeshTexture : public Texture2D {
- GDCLASS(MeshTexture, Texture2D);
- RES_BASE_EXTENSION("meshtex");
-
- Ref<Texture2D> base_texture;
- Ref<Mesh> mesh;
- Size2i size;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_width() const override;
- virtual int get_height() const override;
- virtual RID get_rid() const override;
-
- virtual bool has_alpha() const override;
-
- void set_mesh(const Ref<Mesh> &p_mesh);
- Ref<Mesh> get_mesh() const;
-
- void set_image_size(const Size2 &p_size);
- Size2 get_image_size() const;
-
- void set_base_texture(const Ref<Texture2D> &p_texture);
- Ref<Texture2D> get_base_texture() const;
-
- virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
- virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
- virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
- virtual bool get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const override;
-
- bool is_pixel_opaque(int p_x, int p_y) const override;
-
- MeshTexture();
-};
-
class TextureLayered : public Texture {
GDCLASS(TextureLayered, Texture);
@@ -411,173 +120,6 @@ public:
VARIANT_ENUM_CAST(TextureLayered::LayeredType)
-class ImageTextureLayered : public TextureLayered {
- GDCLASS(ImageTextureLayered, TextureLayered);
-
- LayeredType layered_type;
-
- mutable RID texture;
- Image::Format format = Image::FORMAT_L8;
-
- int width = 0;
- int height = 0;
- int layers = 0;
- bool mipmaps = false;
-
- Error _create_from_images(const TypedArray<Image> &p_images);
-
- TypedArray<Image> _get_images() const;
- void _set_images(const TypedArray<Image> &p_images);
-
-protected:
- static void _bind_methods();
-
-public:
- virtual Image::Format get_format() const override;
- virtual int get_width() const override;
- virtual int get_height() const override;
- virtual int get_layers() const override;
- virtual bool has_mipmaps() const override;
- virtual LayeredType get_layered_type() const override;
-
- Error create_from_images(Vector<Ref<Image>> p_images);
- void update_layer(const Ref<Image> &p_image, int p_layer);
- virtual Ref<Image> get_layer_data(int p_layer) const override;
-
- virtual RID get_rid() const override;
- virtual void set_path(const String &p_path, bool p_take_over = false) override;
-
- ImageTextureLayered(LayeredType p_layered_type);
- ~ImageTextureLayered();
-};
-
-class Texture2DArray : public ImageTextureLayered {
- GDCLASS(Texture2DArray, ImageTextureLayered)
-
-protected:
- static void _bind_methods();
-
-public:
- Texture2DArray() :
- ImageTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
-
- virtual Ref<Resource> create_placeholder() const;
-};
-
-class Cubemap : public ImageTextureLayered {
- GDCLASS(Cubemap, ImageTextureLayered);
-
-protected:
- static void _bind_methods();
-
-public:
- Cubemap() :
- ImageTextureLayered(LAYERED_TYPE_CUBEMAP) {}
-
- virtual Ref<Resource> create_placeholder() const;
-};
-
-class CubemapArray : public ImageTextureLayered {
- GDCLASS(CubemapArray, ImageTextureLayered);
-
-protected:
- static void _bind_methods();
-
-public:
- CubemapArray() :
- ImageTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
-
- virtual Ref<Resource> create_placeholder() const;
-};
-
-class CompressedTextureLayered : public TextureLayered {
- GDCLASS(CompressedTextureLayered, TextureLayered);
-
-public:
- enum DataFormat {
- DATA_FORMAT_IMAGE,
- DATA_FORMAT_PNG,
- DATA_FORMAT_WEBP,
- DATA_FORMAT_BASIS_UNIVERSAL,
- };
-
- enum {
- FORMAT_VERSION = 1
- };
-
- enum FormatBits {
- FORMAT_BIT_STREAM = 1 << 22,
- FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
- };
-
-private:
- Error _load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit = 0);
- String path_to_file;
- mutable RID texture;
- Image::Format format = Image::FORMAT_L8;
- int w = 0;
- int h = 0;
- int layers = 0;
- bool mipmaps = false;
- LayeredType layered_type = LayeredType::LAYERED_TYPE_2D_ARRAY;
-
- virtual void reload_from_file() override;
-
-protected:
- static void _bind_methods();
- void _validate_property(PropertyInfo &p_property) const;
-
-public:
- Image::Format get_format() const override;
- Error load(const String &p_path);
- String get_load_path() const;
- virtual LayeredType get_layered_type() const override;
-
- int get_width() const override;
- int get_height() const override;
- int get_layers() const override;
- virtual bool has_mipmaps() const override;
- virtual RID get_rid() const override;
-
- virtual void set_path(const String &p_path, bool p_take_over) override;
-
- virtual Ref<Image> get_layer_data(int p_layer) const override;
-
- CompressedTextureLayered(LayeredType p_layered_type);
- ~CompressedTextureLayered();
-};
-
-class CompressedTexture2DArray : public CompressedTextureLayered {
- GDCLASS(CompressedTexture2DArray, CompressedTextureLayered)
-public:
- CompressedTexture2DArray() :
- CompressedTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
-};
-
-class CompressedCubemap : public CompressedTextureLayered {
- GDCLASS(CompressedCubemap, CompressedTextureLayered);
-
-public:
- CompressedCubemap() :
- CompressedTextureLayered(LAYERED_TYPE_CUBEMAP) {}
-};
-
-class CompressedCubemapArray : public CompressedTextureLayered {
- GDCLASS(CompressedCubemapArray, CompressedTextureLayered);
-
-public:
- CompressedCubemapArray() :
- CompressedTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
-};
-
-class ResourceFormatLoaderCompressedTextureLayered : public ResourceFormatLoader {
-public:
- virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
-};
-
class Texture3D : public Texture {
GDCLASS(Texture3D, Texture);
@@ -602,495 +144,4 @@ public:
virtual Ref<Resource> create_placeholder() const;
};
-class ImageTexture3D : public Texture3D {
- GDCLASS(ImageTexture3D, Texture3D);
-
- mutable RID texture;
-
- Image::Format format = Image::FORMAT_L8;
- int width = 1;
- int height = 1;
- int depth = 1;
- bool mipmaps = false;
-
-protected:
- static void _bind_methods();
-
- Error _create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const TypedArray<Image> &p_data);
- void _update(const TypedArray<Image> &p_data);
-
-public:
- virtual Image::Format get_format() const override;
- virtual int get_width() const override;
- virtual int get_height() const override;
- virtual int get_depth() const override;
- virtual bool has_mipmaps() const override;
-
- Error create(Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data);
- void update(const Vector<Ref<Image>> &p_data);
- virtual Vector<Ref<Image>> get_data() const override;
-
- virtual RID get_rid() const override;
- virtual void set_path(const String &p_path, bool p_take_over = false) override;
-
- ImageTexture3D();
- ~ImageTexture3D();
-};
-
-class CompressedTexture3D : public Texture3D {
- GDCLASS(CompressedTexture3D, Texture3D);
-
-public:
- enum DataFormat {
- DATA_FORMAT_IMAGE,
- DATA_FORMAT_PNG,
- DATA_FORMAT_WEBP,
- DATA_FORMAT_BASIS_UNIVERSAL,
- };
-
- enum {
- FORMAT_VERSION = 1
- };
-
- enum FormatBits {
- FORMAT_BIT_STREAM = 1 << 22,
- FORMAT_BIT_HAS_MIPMAPS = 1 << 23,
- };
-
-private:
- Error _load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps);
- String path_to_file;
- mutable RID texture;
- Image::Format format = Image::FORMAT_L8;
- int w = 0;
- int h = 0;
- int d = 0;
- bool mipmaps = false;
-
- virtual void reload_from_file() override;
-
-protected:
- static void _bind_methods();
- void _validate_property(PropertyInfo &p_property) const;
-
-public:
- Image::Format get_format() const override;
- Error load(const String &p_path);
- String get_load_path() const;
-
- int get_width() const override;
- int get_height() const override;
- int get_depth() const override;
- virtual bool has_mipmaps() const override;
- virtual RID get_rid() const override;
-
- virtual void set_path(const String &p_path, bool p_take_over) override;
-
- virtual Vector<Ref<Image>> get_data() const override;
-
- CompressedTexture3D();
- ~CompressedTexture3D();
-};
-
-class ResourceFormatLoaderCompressedTexture3D : public ResourceFormatLoader {
-public:
- virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
-};
-
-class CurveTexture : public Texture2D {
- GDCLASS(CurveTexture, Texture2D);
- RES_BASE_EXTENSION("curvetex")
-public:
- enum TextureMode {
- TEXTURE_MODE_RGB,
- TEXTURE_MODE_RED,
- };
-
-private:
- mutable RID _texture;
- Ref<Curve> _curve;
- int _width = 256;
- int _current_width = 0;
- TextureMode texture_mode = TEXTURE_MODE_RGB;
- TextureMode _current_texture_mode = TEXTURE_MODE_RGB;
-
- void _update();
-
-protected:
- static void _bind_methods();
-
-public:
- void set_width(int p_width);
- int get_width() const override;
-
- void set_texture_mode(TextureMode p_mode);
- TextureMode get_texture_mode() const;
-
- void ensure_default_setup(float p_min = 0, float p_max = 1);
-
- void set_curve(Ref<Curve> p_curve);
- Ref<Curve> get_curve() const;
-
- virtual RID get_rid() const override;
-
- virtual int get_height() const override { return 1; }
- virtual bool has_alpha() const override { return false; }
-
- CurveTexture();
- ~CurveTexture();
-};
-
-VARIANT_ENUM_CAST(CurveTexture::TextureMode)
-
-class CurveXYZTexture : public Texture2D {
- GDCLASS(CurveXYZTexture, Texture2D);
- RES_BASE_EXTENSION("curvetex")
-
-private:
- mutable RID _texture;
- Ref<Curve> _curve_x;
- Ref<Curve> _curve_y;
- Ref<Curve> _curve_z;
- int _width = 256;
- int _current_width = 0;
-
- void _update();
-
-protected:
- static void _bind_methods();
-
-public:
- void set_width(int p_width);
- int get_width() const override;
-
- void ensure_default_setup(float p_min = 0, float p_max = 1);
-
- void set_curve_x(Ref<Curve> p_curve);
- Ref<Curve> get_curve_x() const;
-
- void set_curve_y(Ref<Curve> p_curve);
- Ref<Curve> get_curve_y() const;
-
- void set_curve_z(Ref<Curve> p_curve);
- Ref<Curve> get_curve_z() const;
-
- virtual RID get_rid() const override;
-
- virtual int get_height() const override { return 1; }
- virtual bool has_alpha() const override { return false; }
-
- CurveXYZTexture();
- ~CurveXYZTexture();
-};
-
-class GradientTexture1D : public Texture2D {
- GDCLASS(GradientTexture1D, Texture2D);
-
-private:
- Ref<Gradient> gradient;
- bool update_pending = false;
- RID texture;
- int width = 256;
- bool use_hdr = false;
-
- void _queue_update();
- void _update();
-
-protected:
- static void _bind_methods();
-
-public:
- void set_gradient(Ref<Gradient> p_gradient);
- Ref<Gradient> get_gradient() const;
-
- void set_width(int p_width);
- int get_width() const override;
-
- void set_use_hdr(bool p_enabled);
- bool is_using_hdr() const;
-
- virtual RID get_rid() const override { return texture; }
- virtual int get_height() const override { return 1; }
- virtual bool has_alpha() const override { return true; }
-
- virtual Ref<Image> get_image() const override;
-
- GradientTexture1D();
- virtual ~GradientTexture1D();
-};
-
-class GradientTexture2D : public Texture2D {
- GDCLASS(GradientTexture2D, Texture2D);
-
-public:
- enum Fill {
- FILL_LINEAR,
- FILL_RADIAL,
- FILL_SQUARE,
- };
- enum Repeat {
- REPEAT_NONE,
- REPEAT,
- REPEAT_MIRROR,
- };
-
-private:
- Ref<Gradient> gradient;
- mutable RID texture;
-
- int width = 64;
- int height = 64;
-
- bool use_hdr = false;
-
- Vector2 fill_from;
- Vector2 fill_to = Vector2(1, 0);
-
- Fill fill = FILL_LINEAR;
- Repeat repeat = REPEAT_NONE;
-
- float _get_gradient_offset_at(int x, int y) const;
-
- bool update_pending = false;
- void _queue_update();
- void _update();
-
-protected:
- static void _bind_methods();
-
-public:
- void set_gradient(Ref<Gradient> p_gradient);
- Ref<Gradient> get_gradient() const;
-
- void set_width(int p_width);
- virtual int get_width() const override;
- void set_height(int p_height);
- virtual int get_height() const override;
-
- void set_use_hdr(bool p_enabled);
- bool is_using_hdr() const;
-
- void set_fill(Fill p_fill);
- Fill get_fill() const;
- void set_fill_from(Vector2 p_fill_from);
- Vector2 get_fill_from() const;
- void set_fill_to(Vector2 p_fill_to);
- Vector2 get_fill_to() const;
-
- void set_repeat(Repeat p_repeat);
- Repeat get_repeat() const;
-
- virtual RID get_rid() const override;
- virtual bool has_alpha() const override { return true; }
- virtual Ref<Image> get_image() const override;
-
- GradientTexture2D();
- virtual ~GradientTexture2D();
-};
-
-VARIANT_ENUM_CAST(GradientTexture2D::Fill);
-VARIANT_ENUM_CAST(GradientTexture2D::Repeat);
-
-class AnimatedTexture : public Texture2D {
- GDCLASS(AnimatedTexture, Texture2D);
-
- //use readers writers lock for this, since its far more times read than written to
- RWLock rw_lock;
-
-public:
- enum {
- MAX_FRAMES = 256
- };
-
-private:
- RID proxy_ph;
- RID proxy;
-
- struct Frame {
- Ref<Texture2D> texture;
- float duration = 1.0;
- };
-
- Frame frames[MAX_FRAMES];
- int frame_count = 1.0;
- int current_frame = 0;
- bool pause = false;
- bool one_shot = false;
- float speed_scale = 1.0;
-
- float time = 0.0;
-
- uint64_t prev_ticks = 0;
-
- void _update_proxy();
-
-protected:
- static void _bind_methods();
- void _validate_property(PropertyInfo &p_property) const;
-
-public:
- void set_frames(int p_frames);
- int get_frames() const;
-
- void set_current_frame(int p_frame);
- int get_current_frame() const;
-
- void set_pause(bool p_pause);
- bool get_pause() const;
-
- void set_one_shot(bool p_one_shot);
- bool get_one_shot() const;
-
- void set_frame_texture(int p_frame, const Ref<Texture2D> &p_texture);
- Ref<Texture2D> get_frame_texture(int p_frame) const;
-
- void set_frame_duration(int p_frame, float p_duration);
- float get_frame_duration(int p_frame) const;
-
- void set_speed_scale(float p_scale);
- float get_speed_scale() const;
-
- virtual int get_width() const override;
- virtual int get_height() const override;
- virtual RID get_rid() const override;
-
- virtual bool has_alpha() const override;
-
- virtual Ref<Image> get_image() const override;
-
- bool is_pixel_opaque(int p_x, int p_y) const override;
-
- AnimatedTexture();
- ~AnimatedTexture();
-};
-
-class CameraTexture : public Texture2D {
- GDCLASS(CameraTexture, Texture2D);
-
-private:
- mutable RID _texture;
- int camera_feed_id = 0;
- CameraServer::FeedImage which_feed = CameraServer::FEED_RGBA_IMAGE;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_width() const override;
- virtual int get_height() const override;
- virtual RID get_rid() const override;
- virtual bool has_alpha() const override;
-
- virtual Ref<Image> get_image() const override;
-
- void set_camera_feed_id(int p_new_id);
- int get_camera_feed_id() const;
-
- void set_which_feed(CameraServer::FeedImage p_which);
- CameraServer::FeedImage get_which_feed() const;
-
- void set_camera_active(bool p_active);
- bool get_camera_active() const;
-
- CameraTexture();
- ~CameraTexture();
-};
-
-class PlaceholderTexture2D : public Texture2D {
- GDCLASS(PlaceholderTexture2D, Texture2D)
-
- RID rid;
- Size2 size = Size2(1, 1);
-
-protected:
- static void _bind_methods();
-
-public:
- void set_size(Size2 p_size);
-
- virtual int get_width() const override;
- virtual int get_height() const override;
- virtual RID get_rid() const override;
- virtual bool has_alpha() const override;
-
- virtual Ref<Image> get_image() const override;
-
- PlaceholderTexture2D();
- ~PlaceholderTexture2D();
-};
-
-class PlaceholderTexture3D : public Texture3D {
- GDCLASS(PlaceholderTexture3D, Texture3D)
-
- RID rid;
- Vector3i size = Vector3i(1, 1, 1);
-
-protected:
- static void _bind_methods();
-
-public:
- void set_size(const Vector3i &p_size);
- Vector3i get_size() const;
- virtual Image::Format get_format() const override;
- virtual int get_width() const override;
- virtual int get_height() const override;
- virtual int get_depth() const override;
- virtual bool has_mipmaps() const override;
- virtual Vector<Ref<Image>> get_data() const override;
-
- PlaceholderTexture3D();
- ~PlaceholderTexture3D();
-};
-
-class PlaceholderTextureLayered : public TextureLayered {
- GDCLASS(PlaceholderTextureLayered, TextureLayered)
-
- RID rid;
- Size2i size = Size2i(1, 1);
- int layers = 1;
- LayeredType layered_type = LAYERED_TYPE_2D_ARRAY;
-
-protected:
- static void _bind_methods();
-
-public:
- void set_size(const Size2i &p_size);
- Size2i get_size() const;
- void set_layers(int p_layers);
- virtual Image::Format get_format() const override;
- virtual LayeredType get_layered_type() const override;
- virtual int get_width() const override;
- virtual int get_height() const override;
- virtual int get_layers() const override;
- virtual bool has_mipmaps() const override;
- virtual Ref<Image> get_layer_data(int p_layer) const override;
-
- PlaceholderTextureLayered(LayeredType p_type);
- ~PlaceholderTextureLayered();
-};
-
-class PlaceholderTexture2DArray : public PlaceholderTextureLayered {
- GDCLASS(PlaceholderTexture2DArray, PlaceholderTextureLayered)
-public:
- PlaceholderTexture2DArray() :
- PlaceholderTextureLayered(LAYERED_TYPE_2D_ARRAY) {}
-};
-
-class PlaceholderCubemap : public PlaceholderTextureLayered {
- GDCLASS(PlaceholderCubemap, PlaceholderTextureLayered)
-public:
- PlaceholderCubemap() :
- PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP) {}
-};
-
-class PlaceholderCubemapArray : public PlaceholderTextureLayered {
- GDCLASS(PlaceholderCubemapArray, PlaceholderTextureLayered)
-public:
- PlaceholderCubemapArray() :
- PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {}
-};
-
#endif // TEXTURE_H
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index bcbc8b94e7..799a8471b9 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -222,13 +222,13 @@ void Theme::set_default_font(const Ref<Font> &p_default_font) {
}
if (default_font.is_valid()) {
- default_font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ default_font->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
default_font = p_default_font;
if (default_font.is_valid()) {
- default_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
+ default_font->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed();
@@ -268,13 +268,13 @@ void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, c
bool existing = false;
if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
existing = true;
- icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ icon_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
icon_map[p_theme_type][p_name] = p_icon;
if (p_icon.is_valid()) {
- icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
+ icon_map[p_theme_type][p_name]->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed(!existing);
@@ -314,7 +314,7 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type)
ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_name), "Cannot clear the icon '" + String(p_name) + "' because it does not exist.");
if (icon_map[p_theme_type][p_name].is_valid()) {
- icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ icon_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
icon_map[p_theme_type].erase(p_name);
@@ -353,7 +353,7 @@ void Theme::remove_icon_type(const StringName &p_theme_type) {
for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) {
Ref<Texture2D> icon = E.value;
if (icon.is_valid()) {
- icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ icon->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
}
@@ -378,13 +378,13 @@ void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_typ
bool existing = false;
if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
existing = true;
- style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ style_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
style_map[p_theme_type][p_name] = p_style;
if (p_style.is_valid()) {
- style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
+ style_map[p_theme_type][p_name]->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed(!existing);
@@ -424,7 +424,7 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_t
ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_name), "Cannot clear the stylebox '" + String(p_name) + "' because it does not exist.");
if (style_map[p_theme_type][p_name].is_valid()) {
- style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ style_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
style_map[p_theme_type].erase(p_name);
@@ -463,7 +463,7 @@ void Theme::remove_stylebox_type(const StringName &p_theme_type) {
for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) {
Ref<StyleBox> style = E.value;
if (style.is_valid()) {
- style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ style->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
}
@@ -488,13 +488,13 @@ void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, c
bool existing = false;
if (font_map[p_theme_type][p_name].is_valid()) {
existing = true;
- font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ font_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
font_map[p_theme_type][p_name] = p_font;
if (p_font.is_valid()) {
- font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
+ font_map[p_theme_type][p_name]->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed(!existing);
@@ -536,7 +536,7 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type)
ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_name), "Cannot clear the font '" + String(p_name) + "' because it does not exist.");
if (font_map[p_theme_type][p_name].is_valid()) {
- font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ font_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
font_map[p_theme_type].erase(p_name);
@@ -575,7 +575,7 @@ void Theme::remove_font_type(const StringName &p_theme_type) {
for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) {
Ref<Font> font = E.value;
if (font.is_valid()) {
- font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ font->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
}
@@ -1622,7 +1622,7 @@ void Theme::clear() {
for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) {
if (F.value.is_valid()) {
Ref<Texture2D> icon = F.value;
- icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ icon->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
}
}
@@ -1633,7 +1633,7 @@ void Theme::clear() {
for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) {
if (F.value.is_valid()) {
Ref<StyleBox> style = F.value;
- style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ style->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
}
}
@@ -1644,7 +1644,7 @@ void Theme::clear() {
for (const KeyValue<StringName, Ref<Font>> &F : E.value) {
if (F.value.is_valid()) {
Ref<Font> font = F.value;
- font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ font->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
}
}
}
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 3c0fb6b9fa..19f57dda00 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -30,13 +30,13 @@
#include "tile_set.h"
-#include "core/core_string_names.h"
#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/templates/local_vector.h"
#include "core/templates/rb_set.h"
#include "scene/gui/control.h"
#include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/image_texture.h"
#include "servers/navigation_server_2d.h"
/////////////////////////////// TileMapPattern //////////////////////////////////////
@@ -487,7 +487,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source
p_tile_set_source->set_tile_set(this);
_compute_next_source_id();
- sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed));
+ sources[new_source_id]->connect_changed(callable_mp(this, &TileSet::_source_changed));
terrains_cache_dirty = true;
emit_changed();
@@ -498,7 +498,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source
void TileSet::remove_source(int p_source_id) {
ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot remove TileSet atlas source. No tileset atlas source with id %d.", p_source_id));
- sources[p_source_id]->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed));
+ sources[p_source_id]->disconnect_changed(callable_mp(this, &TileSet::_source_changed));
sources[p_source_id]->set_tile_set(nullptr);
sources.erase(p_source_id);
@@ -3814,13 +3814,13 @@ void TileSetAtlasSource::reset_state() {
void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
if (texture.is_valid()) {
- texture->disconnect(SNAME("changed"), callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
+ texture->disconnect_changed(callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
}
texture = p_texture;
if (texture.is_valid()) {
- texture->connect(SNAME("changed"), callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
+ texture->connect_changed(callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture));
}
_clear_tiles_outside_texture();
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 70646ca5d8..4150da53db 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -39,6 +39,7 @@
#include "scene/main/canvas_item.h"
#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/image_texture.h"
#include "scene/resources/navigation_polygon.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/physics_material.h"
@@ -46,7 +47,6 @@
#ifndef DISABLE_DEPRECATED
#include "scene/resources/shader.h"
-#include "scene/resources/texture.h"
#endif
class TileMap;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 0c2c21380a..7f1c322c8f 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -46,6 +46,10 @@ bool VisualShaderNode::is_simple_decl() const {
return simple_decl;
}
+int VisualShaderNode::get_default_input_port(PortType p_type) const {
+ return 0;
+}
+
void VisualShaderNode::set_output_port_for_preview(int p_index) {
port_preview = p_index;
}
@@ -378,6 +382,8 @@ bool VisualShaderNode::is_input_port_default(int p_port, Shader::Mode p_mode) co
}
void VisualShaderNode::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_default_input_port", "type"), &VisualShaderNode::get_default_input_port);
+
ClassDB::bind_method(D_METHOD("set_output_port_for_preview", "port"), &VisualShaderNode::set_output_port_for_preview);
ClassDB::bind_method(D_METHOD("get_output_port_for_preview"), &VisualShaderNode::get_output_port_for_preview);
@@ -481,6 +487,12 @@ String VisualShaderNodeCustom::get_input_port_name(int p_port) const {
return input_ports[p_port].name;
}
+int VisualShaderNodeCustom::get_default_input_port(PortType p_type) const {
+ int ret = 0;
+ GDVIRTUAL_CALL(_get_default_input_port, p_type, ret);
+ return ret;
+}
+
int VisualShaderNodeCustom::get_output_port_count() const {
return output_ports.size();
}
@@ -649,6 +661,7 @@ void VisualShaderNodeCustom::_bind_methods() {
GDVIRTUAL_BIND(_get_input_port_count);
GDVIRTUAL_BIND(_get_input_port_type, "port");
GDVIRTUAL_BIND(_get_input_port_name, "port");
+ GDVIRTUAL_BIND(_get_default_input_port, "type");
GDVIRTUAL_BIND(_get_output_port_count);
GDVIRTUAL_BIND(_get_output_port_type, "port");
GDVIRTUAL_BIND(_get_output_port_name, "port");
@@ -767,7 +780,7 @@ void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, co
input->shader_type = p_type;
}
- n.node->connect("changed", callable_mp(this, &VisualShader::_queue_update));
+ n.node->connect_changed(callable_mp(this, &VisualShader::_queue_update));
Ref<VisualShaderNodeCustom> custom = n.node;
if (custom.is_valid()) {
@@ -834,7 +847,7 @@ void VisualShader::remove_node(Type p_type, int p_id) {
Graph *g = &graph[p_type];
ERR_FAIL_COND(!g->nodes.has(p_id));
- g->nodes[p_id].node->disconnect("changed", callable_mp(this, &VisualShader::_queue_update));
+ g->nodes[p_id].node->disconnect_changed(callable_mp(this, &VisualShader::_queue_update));
g->nodes.erase(p_id);
@@ -907,7 +920,7 @@ void VisualShader::replace_node(Type p_type, int p_id, const StringName &p_new_c
}
}
- vsn->connect("changed", callable_mp(this, &VisualShader::_queue_update));
+ vsn->connect_changed(callable_mp(this, &VisualShader::_queue_update));
g->nodes[p_id].node = Ref<VisualShaderNode>(vsn);
_queue_update();
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 61418b680e..d3657eae07 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -289,6 +289,7 @@ public:
virtual int get_input_port_count() const = 0;
virtual PortType get_input_port_type(int p_port) const = 0;
virtual String get_input_port_name(int p_port) const = 0;
+ virtual int get_default_input_port(PortType p_type) const;
virtual void set_input_port_default_value(int p_port, const Variant &p_value, const Variant &p_prev_value = Variant());
Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied)
@@ -367,6 +368,7 @@ protected:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
+ virtual int get_default_input_port(PortType p_type) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
@@ -384,6 +386,7 @@ protected:
GDVIRTUAL0RC(int, _get_input_port_count)
GDVIRTUAL1RC(PortType, _get_input_port_type, int)
GDVIRTUAL1RC(String, _get_input_port_name, int)
+ GDVIRTUAL1RC(int, _get_default_input_port, PortType)
GDVIRTUAL0RC(int, _get_output_port_count)
GDVIRTUAL1RC(PortType, _get_output_port_type, int)
GDVIRTUAL1RC(String, _get_output_port_name, int)
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 7fdad8e930..9d56895720 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -30,6 +30,8 @@
#include "visual_shader_nodes.h"
+#include "scene/resources/image_texture.h"
+
////////////// Vector Base
VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_input_port_type(int p_port) const {
@@ -4135,6 +4137,10 @@ String VisualShaderNodeStep::get_input_port_name(int p_port) const {
return String();
}
+int VisualShaderNodeStep::get_default_input_port(PortType p_type) const {
+ return 1;
+}
+
int VisualShaderNodeStep::get_output_port_count() const {
return 1;
}
@@ -4290,6 +4296,10 @@ String VisualShaderNodeSmoothStep::get_input_port_name(int p_port) const {
return String();
}
+int VisualShaderNodeSmoothStep::get_default_input_port(PortType p_type) const {
+ return 2;
+}
+
int VisualShaderNodeSmoothStep::get_output_port_count() const {
return 1;
}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index fa6b134526..4973db0a22 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -31,8 +31,12 @@
#ifndef VISUAL_SHADER_NODES_H
#define VISUAL_SHADER_NODES_H
+#include "scene/resources/curve_texture.h"
#include "scene/resources/visual_shader.h"
+class Cubemap;
+class Texture2DArray;
+
///////////////////////////////////////
/// Vector Base Node
///////////////////////////////////////
@@ -1657,6 +1661,7 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
+ virtual int get_default_input_port(PortType p_type) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
@@ -1703,6 +1708,7 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
+ virtual int get_default_input_port(PortType p_type) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 9cf42b681c..cfea6e21ee 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -30,7 +30,7 @@
#include "visual_shader_particle_nodes.h"
-#include "core/core_string_names.h"
+#include "scene/resources/image_texture.h"
// VisualShaderNodeParticleEmitter
@@ -637,21 +637,13 @@ void VisualShaderNodeParticleMeshEmitter::set_mesh(Ref<Mesh> p_mesh) {
}
if (mesh.is_valid()) {
- Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures);
-
- if (mesh->is_connected(CoreStringNames::get_singleton()->changed, callable)) {
- mesh->disconnect(CoreStringNames::get_singleton()->changed, callable);
- }
+ mesh->disconnect_changed(callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures));
}
mesh = p_mesh;
if (mesh.is_valid()) {
- Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures);
-
- if (!mesh->is_connected(CoreStringNames::get_singleton()->changed, callable)) {
- mesh->connect(CoreStringNames::get_singleton()->changed, callable);
- }
+ mesh->connect_changed(callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures));
}
emit_changed();
@@ -732,7 +724,7 @@ void VisualShaderNodeParticleMeshEmitter::_bind_methods() {
}
VisualShaderNodeParticleMeshEmitter::VisualShaderNodeParticleMeshEmitter() {
- connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures));
+ connect_changed(callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures));
position_texture.instantiate();
normal_texture.instantiate();
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index 08fb059534..652b5dff03 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -33,6 +33,8 @@
#include "scene/resources/visual_shader.h"
+class ImageTexture;
+
// Emit nodes
class VisualShaderNodeParticleEmitter : public VisualShaderNode {
diff --git a/scene/resources/world_boundary_shape_3d.cpp b/scene/resources/world_boundary_shape_3d.cpp
index 3074bd1fd8..beaaddc95e 100644
--- a/scene/resources/world_boundary_shape_3d.cpp
+++ b/scene/resources/world_boundary_shape_3d.cpp
@@ -69,7 +69,7 @@ void WorldBoundaryShape3D::_update_shape() {
void WorldBoundaryShape3D::set_plane(const Plane &p_plane) {
plane = p_plane;
_update_shape();
- notify_change_to_owners();
+ emit_changed();
}
const Plane &WorldBoundaryShape3D::get_plane() const {
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index 536ffd1fe4..87835a9522 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -57,7 +57,6 @@ SceneStringNames::SceneStringNames() {
sleeping_state_changed = StaticCString::create("sleeping_state_changed");
finished = StaticCString::create("finished");
- emission_finished = StaticCString::create("emission_finished");
animation_finished = StaticCString::create("animation_finished");
animation_changed = StaticCString::create("animation_changed");
animation_started = StaticCString::create("animation_started");
@@ -168,8 +167,6 @@ SceneStringNames::SceneStringNames() {
_drop_data = StaticCString::create("_drop_data");
_can_drop_data = StaticCString::create("_can_drop_data");
- _im_update = StaticCString::create("_im_update"); // Sprite3D
-
baked_light_changed = StaticCString::create("baked_light_changed");
_baked_light_changed = StaticCString::create("_baked_light_changed");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index ca8f7a1e7d..ad1135e24c 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -93,7 +93,6 @@ public:
StringName sort_children;
StringName finished;
- StringName emission_finished;
StringName animation_finished;
StringName animation_changed;
StringName animation_started;
@@ -180,8 +179,6 @@ public:
StringName _get_minimum_size;
- StringName _im_update;
-
StringName baked_light_changed;
StringName _baked_light_changed;
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 313e7218ed..5822a630db 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -497,6 +497,11 @@ Error DisplayServer::dialog_input_text(String p_title, String p_description, Str
return OK;
}
+Error DisplayServer::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) {
+ WARN_PRINT("Native dialogs not supported by this display server.");
+ return OK;
+}
+
int DisplayServer::keyboard_get_layout_count() const {
return 0;
}
@@ -755,6 +760,8 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("dialog_show", "title", "description", "buttons", "callback"), &DisplayServer::dialog_show);
ClassDB::bind_method(D_METHOD("dialog_input_text", "title", "description", "existing_text", "callback"), &DisplayServer::dialog_input_text);
+ ClassDB::bind_method(D_METHOD("file_dialog_show", "title", "current_directory", "filename", "show_hidden", "mode", "filters", "callback"), &DisplayServer::file_dialog_show);
+
ClassDB::bind_method(D_METHOD("keyboard_get_layout_count"), &DisplayServer::keyboard_get_layout_count);
ClassDB::bind_method(D_METHOD("keyboard_get_current_layout"), &DisplayServer::keyboard_get_current_layout);
ClassDB::bind_method(D_METHOD("keyboard_set_current_layout", "index"), &DisplayServer::keyboard_set_current_layout);
@@ -846,6 +853,12 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(CURSOR_HELP);
BIND_ENUM_CONSTANT(CURSOR_MAX);
+ BIND_ENUM_CONSTANT(FILE_DIALOG_MODE_OPEN_FILE);
+ BIND_ENUM_CONSTANT(FILE_DIALOG_MODE_OPEN_FILES);
+ BIND_ENUM_CONSTANT(FILE_DIALOG_MODE_OPEN_DIR);
+ BIND_ENUM_CONSTANT(FILE_DIALOG_MODE_OPEN_ANY);
+ BIND_ENUM_CONSTANT(FILE_DIALOG_MODE_SAVE_FILE);
+
BIND_ENUM_CONSTANT(WINDOW_MODE_WINDOWED);
BIND_ENUM_CONSTANT(WINDOW_MODE_MINIMIZED);
BIND_ENUM_CONSTANT(WINDOW_MODE_MAXIMIZED);
diff --git a/servers/display_server.h b/servers/display_server.h
index e6d9c51a67..5db9b3231f 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -493,6 +493,15 @@ public:
virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback);
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback);
+ enum FileDialogMode {
+ FILE_DIALOG_MODE_OPEN_FILE,
+ FILE_DIALOG_MODE_OPEN_FILES,
+ FILE_DIALOG_MODE_OPEN_DIR,
+ FILE_DIALOG_MODE_OPEN_ANY,
+ FILE_DIALOG_MODE_SAVE_FILE
+ };
+ virtual Error 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);
+
virtual int keyboard_get_layout_count() const;
virtual int keyboard_get_current_layout() const;
virtual void keyboard_set_current_layout(int p_index);
@@ -546,5 +555,6 @@ VARIANT_ENUM_CAST(DisplayServer::VirtualKeyboardType);
VARIANT_ENUM_CAST(DisplayServer::CursorShape)
VARIANT_ENUM_CAST(DisplayServer::VSyncMode)
VARIANT_ENUM_CAST(DisplayServer::TTSUtteranceEvent)
+VARIANT_ENUM_CAST(DisplayServer::FileDialogMode)
#endif // DISPLAY_SERVER_H
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index 3b26945c86..4da479639d 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -185,10 +185,10 @@ NavigationServer3D::NavigationServer3D() {
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
- GLOBAL_DEF_BASIC("navigation/2d/default_cell_size", 1);
+ GLOBAL_DEF_BASIC("navigation/2d/default_cell_size", 1.0);
GLOBAL_DEF("navigation/2d/use_edge_connections", true);
- GLOBAL_DEF_BASIC("navigation/2d/default_edge_connection_margin", 1);
- GLOBAL_DEF_BASIC("navigation/2d/default_link_connection_radius", 4);
+ GLOBAL_DEF_BASIC("navigation/2d/default_edge_connection_margin", 1.0);
+ GLOBAL_DEF_BASIC("navigation/2d/default_link_connection_radius", 4.0);
GLOBAL_DEF_BASIC("navigation/3d/default_cell_size", 0.25);
GLOBAL_DEF_BASIC("navigation/3d/default_cell_height", 0.25);
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp
index 86484c982a..eba1c145e3 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp
@@ -962,7 +962,7 @@ void CopyEffects::set_color_raster(RID p_dest_texture, const Color &p_color, con
RD::get_singleton()->draw_list_end();
}
-void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip) {
+void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip, BitField<RD::BarrierMask> p_post_barrier) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
MaterialStorage *material_storage = MaterialStorage::get_singleton();
@@ -994,7 +994,7 @@ void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuf
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(CopyToDPPushConstant));
RD::get_singleton()->draw_list_draw(draw_list, true);
- RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_TRANSFER);
+ RD::get_singleton()->draw_list_end(p_post_barrier);
}
void CopyEffects::cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size) {
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h
index 3cd26d0f7e..470ac1acee 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.h
+++ b/servers/rendering/renderer_rd/effects/copy_effects.h
@@ -341,7 +341,7 @@ public:
void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false);
void set_color_raster(RID p_dest_texture, const Color &p_color, const Rect2i &p_region);
- void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip);
+ void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip, BitField<RD::BarrierMask> p_post_barrier = RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_TRANSFER);
void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size);
void cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, const Size2i &p_size);
void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array);
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 618348c688..c29204ed1e 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -1245,14 +1245,15 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i
_render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info);
if (finalize_cubemap) {
_render_shadow_process();
- _render_shadow_end();
- //reblit
+ _render_shadow_end(RD::BARRIER_MASK_FRAGMENT);
+
+ // reblit
Rect2 atlas_rect_norm = atlas_rect;
atlas_rect_norm.position /= float(atlas_size);
atlas_rect_norm.size /= float(atlas_size);
- copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false);
+ copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false, RD::BARRIER_MASK_NO_BARRIER);
atlas_rect_norm.position += Vector2(dual_paraboloid_offset) * atlas_rect_norm.size;
- copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true);
+ copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true, RD::BARRIER_MASK_NO_BARRIER);
//restore transform so it can be properly used
light_storage->light_instance_set_shadow_transform(p_light, Projection(), light_storage->light_instance_get_base_transform(p_light), zfar, 0, 0, 0);
@@ -1362,7 +1363,7 @@ void RenderForwardMobile::_render_shadow_end(uint32_t p_barrier) {
}
if (p_barrier != RD::BARRIER_MASK_NO_BARRIER) {
- RD::get_singleton()->barrier(RD::BARRIER_MASK_RASTER, p_barrier);
+ RD::get_singleton()->barrier(RD::BARRIER_MASK_FRAGMENT, p_barrier);
}
RD::get_singleton()->draw_command_end_label();
}
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index d87c09a857..2dd11e5efe 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -1073,9 +1073,11 @@ void RenderingDevice::_bind_methods() {
BIND_ENUM_CONSTANT(DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM);
BIND_ENUM_CONSTANT(DATA_FORMAT_MAX);
- BIND_BITFIELD_FLAG(BARRIER_MASK_RASTER);
+ BIND_BITFIELD_FLAG(BARRIER_MASK_VERTEX);
+ BIND_BITFIELD_FLAG(BARRIER_MASK_FRAGMENT);
BIND_BITFIELD_FLAG(BARRIER_MASK_COMPUTE);
BIND_BITFIELD_FLAG(BARRIER_MASK_TRANSFER);
+ BIND_BITFIELD_FLAG(BARRIER_MASK_RASTER);
BIND_BITFIELD_FLAG(BARRIER_MASK_ALL_BARRIERS);
BIND_BITFIELD_FLAG(BARRIER_MASK_NO_BARRIER);
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index dd2f8d55aa..0a362962a1 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -393,11 +393,14 @@ public:
/*****************/
enum BarrierMask {
- BARRIER_MASK_RASTER = 1,
- BARRIER_MASK_COMPUTE = 2,
- BARRIER_MASK_TRANSFER = 4,
+ BARRIER_MASK_VERTEX = 1,
+ BARRIER_MASK_FRAGMENT = 2,
+ BARRIER_MASK_COMPUTE = 4,
+ BARRIER_MASK_TRANSFER = 8,
+
+ BARRIER_MASK_RASTER = BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT, // 3,
BARRIER_MASK_ALL_BARRIERS = BARRIER_MASK_RASTER | BARRIER_MASK_COMPUTE | BARRIER_MASK_TRANSFER, // 7
- BARRIER_MASK_NO_BARRIER = 8,
+ BARRIER_MASK_NO_BARRIER = 16,
};
/*****************/
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 65bdb94c9f..ff6bf3a6ba 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -258,22 +258,10 @@ uint64_t RenderingServerDefault::get_rendering_info(RenderingInfo p_info) {
return RSG::utilities->get_rendering_info(p_info);
}
-String RenderingServerDefault::get_video_adapter_name() const {
- return RSG::utilities->get_video_adapter_name();
-}
-
-String RenderingServerDefault::get_video_adapter_vendor() const {
- return RSG::utilities->get_video_adapter_vendor();
-}
-
RenderingDevice::DeviceType RenderingServerDefault::get_video_adapter_type() const {
return RSG::utilities->get_video_adapter_type();
}
-String RenderingServerDefault::get_video_adapter_api_version() const {
- return RSG::utilities->get_video_adapter_api_version();
-}
-
void RenderingServerDefault::set_frame_profiling_enabled(bool p_enable) {
RSG::utilities->capturing_timestamps = p_enable;
}
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 797fe73ba0..340eb7394b 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -946,9 +946,26 @@ public:
#undef server_name
#undef ServerName
+ /* STATUS INFORMATION */
+#define ServerName RendererUtilities
+#define server_name RSG::utilities
+ FUNC0RC(String, get_video_adapter_name)
+ FUNC0RC(String, get_video_adapter_vendor)
+ FUNC0RC(String, get_video_adapter_api_version)
+#undef server_name
+#undef ServerName
#undef WRITE_ACTION
#undef SYNC_DEBUG
+ virtual uint64_t get_rendering_info(RenderingInfo p_info) override;
+ virtual RenderingDevice::DeviceType get_video_adapter_type() const override;
+
+ virtual void set_frame_profiling_enabled(bool p_enable) override;
+ virtual Vector<FrameProfileArea> get_frame_profile() override;
+ virtual uint64_t get_frame_profile_frame() override;
+
+ virtual RID get_test_cube() override;
+
/* FREE */
virtual void free(RID p_rid) override {
@@ -970,20 +987,6 @@ public:
virtual void init() override;
virtual void finish() override;
- /* STATUS INFORMATION */
-
- virtual uint64_t get_rendering_info(RenderingInfo p_info) override;
- virtual String get_video_adapter_name() const override;
- virtual String get_video_adapter_vendor() const override;
- virtual RenderingDevice::DeviceType get_video_adapter_type() const override;
- virtual String get_video_adapter_api_version() const override;
-
- virtual void set_frame_profiling_enabled(bool p_enable) override;
- virtual Vector<FrameProfileArea> get_frame_profile() override;
- virtual uint64_t get_frame_profile_frame() override;
-
- virtual RID get_test_cube() override;
-
/* TESTING */
virtual double get_frame_setup_time_cpu() const override;
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index ff4f670c58..04292e1f8c 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -10549,19 +10549,23 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
}
} else if ((int(completion_base) > int(TYPE_MAT4) && int(completion_base) < int(TYPE_STRUCT))) {
Vector<String> options;
+ if (current_uniform_filter == FILTER_DEFAULT) {
+ options.push_back("filter_linear");
+ options.push_back("filter_linear_mipmap");
+ options.push_back("filter_linear_mipmap_anisotropic");
+ options.push_back("filter_nearest");
+ options.push_back("filter_nearest_mipmap");
+ options.push_back("filter_nearest_mipmap_anisotropic");
+ }
+ if (current_uniform_repeat == REPEAT_DEFAULT) {
+ options.push_back("repeat_enable");
+ options.push_back("repeat_disable");
+ }
if (completion_base_array) {
if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) {
options.push_back("source_color");
}
} else {
- if (current_uniform_filter == FILTER_DEFAULT) {
- options.push_back("filter_linear");
- options.push_back("filter_linear_mipmap");
- options.push_back("filter_linear_mipmap_anisotropic");
- options.push_back("filter_nearest");
- options.push_back("filter_nearest_mipmap");
- options.push_back("filter_nearest_mipmap_anisotropic");
- }
if (current_uniform_hint == ShaderNode::Uniform::HINT_NONE) {
options.push_back("hint_anisotropy");
options.push_back("hint_default_black");
@@ -10579,10 +10583,6 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
options.push_back("hint_depth_texture");
options.push_back("source_color");
}
- if (current_uniform_repeat == REPEAT_DEFAULT) {
- options.push_back("repeat_enable");
- options.push_back("repeat_disable");
- }
}
for (int i = 0; i < options.size(); i++) {
diff --git a/tests/core/io/test_resource.h b/tests/core/io/test_resource.h
index 20d76c8894..8fc2a2f040 100644
--- a/tests/core/io/test_resource.h
+++ b/tests/core/io/test_resource.h
@@ -109,6 +109,58 @@ TEST_CASE("[Resource] Saving and loading") {
loaded_child_resource_text->get_name() == "I'm a child resource",
"The loaded child resource name should be equal to the expected value.");
}
+
+TEST_CASE("[Resource] Breaking circular references on save") {
+ Ref<Resource> resource_a = memnew(Resource);
+ resource_a->set_name("A");
+ Ref<Resource> resource_b = memnew(Resource);
+ resource_b->set_name("B");
+ Ref<Resource> resource_c = memnew(Resource);
+ resource_c->set_name("C");
+ resource_a->set_meta("next", resource_b);
+ resource_b->set_meta("next", resource_c);
+ resource_c->set_meta("next", resource_b);
+
+ const String save_path_binary = OS::get_singleton()->get_cache_path().path_join("resource.res");
+ const String save_path_text = OS::get_singleton()->get_cache_path().path_join("resource.tres");
+ ResourceSaver::save(resource_a, save_path_binary);
+ ResourceSaver::save(resource_a, save_path_text);
+
+ const Ref<Resource> &loaded_resource_a_binary = ResourceLoader::load(save_path_binary);
+ CHECK_MESSAGE(
+ loaded_resource_a_binary->get_name() == "A",
+ "The loaded resource name should be equal to the expected value.");
+ const Ref<Resource> &loaded_resource_b_binary = loaded_resource_a_binary->get_meta("next");
+ CHECK_MESSAGE(
+ loaded_resource_b_binary->get_name() == "B",
+ "The loaded child resource name should be equal to the expected value.");
+ const Ref<Resource> &loaded_resource_c_binary = loaded_resource_b_binary->get_meta("next");
+ CHECK_MESSAGE(
+ loaded_resource_c_binary->get_name() == "C",
+ "The loaded child resource name should be equal to the expected value.");
+ CHECK_MESSAGE(
+ !loaded_resource_c_binary->get_meta("next"),
+ "The loaded child resource circular reference should be NULL.");
+
+ const Ref<Resource> &loaded_resource_a_text = ResourceLoader::load(save_path_text);
+ CHECK_MESSAGE(
+ loaded_resource_a_text->get_name() == "A",
+ "The loaded resource name should be equal to the expected value.");
+ const Ref<Resource> &loaded_resource_b_text = loaded_resource_a_text->get_meta("next");
+ CHECK_MESSAGE(
+ loaded_resource_b_text->get_name() == "B",
+ "The loaded child resource name should be equal to the expected value.");
+ const Ref<Resource> &loaded_resource_c_text = loaded_resource_b_text->get_meta("next");
+ CHECK_MESSAGE(
+ loaded_resource_c_text->get_name() == "C",
+ "The loaded child resource name should be equal to the expected value.");
+ CHECK_MESSAGE(
+ !loaded_resource_c_text->get_meta("next"),
+ "The loaded child resource circular reference should be NULL.");
+
+ // Break circular reference to avoid memory leak
+ resource_c->remove_meta("next");
+}
} // namespace TestResource
#endif // TEST_RESOURCE_H
diff --git a/tests/core/threads/test_worker_thread_pool.h b/tests/core/threads/test_worker_thread_pool.h
index e9a762b57b..ef0b475715 100644
--- a/tests/core/threads/test_worker_thread_pool.h
+++ b/tests/core/threads/test_worker_thread_pool.h
@@ -106,6 +106,32 @@ TEST_CASE("[WorkerThreadPool] Process elements using group tasks") {
}
}
+TEST_CASE("[WorkerThreadPool] Parallel foreach") {
+ const int count_max = 256;
+
+ for (int midpoint = 0; midpoint < count_max; midpoint++) {
+ LocalVector<int> c;
+ c.resize(count_max);
+
+ for_range(0, count_max, true, String(), [&](int i) {
+ c[i] = 1;
+ });
+ c.sort();
+ CHECK(c[0] == 1);
+ CHECK(c[0] == c[count_max - 1]);
+
+ for_range(0, midpoint, false, String(), [&](int i) {
+ c[i]++;
+ });
+ for_range(midpoint, count_max, true, String(), [&](int i) {
+ c[i]++;
+ });
+ c.sort();
+ CHECK(c[0] == 2);
+ CHECK(c[0] == c[count_max - 1]);
+ }
+}
+
} // namespace TestWorkerThreadPool
#endif // TEST_WORKER_THREAD_POOL_H
diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h
index ccb02ed5fa..228d77b3b5 100644
--- a/tests/core/variant/test_array.h
+++ b/tests/core/variant/test_array.h
@@ -304,13 +304,31 @@ TEST_CASE("[Array] slice()") {
CHECK(slice8[1] == Variant(3));
CHECK(slice8[2] == Variant(1));
+ Array slice9 = array.slice(10, 0, -2);
+ CHECK(slice9.size() == 3);
+ CHECK(slice9[0] == Variant(5));
+ CHECK(slice9[1] == Variant(3));
+ CHECK(slice9[2] == Variant(1));
+
+ Array slice10 = array.slice(2, -10, -1);
+ CHECK(slice10.size() == 3);
+ CHECK(slice10[0] == Variant(2));
+ CHECK(slice10[1] == Variant(1));
+ CHECK(slice10[2] == Variant(0));
+
ERR_PRINT_OFF;
- Array slice9 = array.slice(4, 1);
- CHECK(slice9.size() == 0);
+ Array slice11 = array.slice(4, 1);
+ CHECK(slice11.size() == 0);
- Array slice10 = array.slice(3, -4);
- CHECK(slice10.size() == 0);
+ Array slice12 = array.slice(3, -4);
+ CHECK(slice12.size() == 0);
ERR_PRINT_ON;
+
+ Array slice13 = Array().slice(1);
+ CHECK(slice13.size() == 0);
+
+ Array slice14 = array.slice(6);
+ CHECK(slice14.size() == 0);
}
TEST_CASE("[Array] Duplicate array") {
diff --git a/tests/scene/test_theme.h b/tests/scene/test_theme.h
index 69d29e5ca5..ad1ce1fd50 100644
--- a/tests/scene/test_theme.h
+++ b/tests/scene/test_theme.h
@@ -31,6 +31,8 @@
#ifndef TEST_THEME_H
#define TEST_THEME_H
+#include "scene/resources/image_texture.h"
+#include "scene/resources/style_box_flat.h"
#include "scene/resources/theme.h"
#include "tests/test_tools.h"
diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h
index ffd5b83231..8ea69ec67c 100644
--- a/tests/servers/test_navigation_server_3d.h
+++ b/tests/servers/test_navigation_server_3d.h
@@ -31,11 +31,38 @@
#ifndef TEST_NAVIGATION_SERVER_3D_H
#define TEST_NAVIGATION_SERVER_3D_H
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/resources/primitive_meshes.h"
#include "servers/navigation_server_3d.h"
#include "tests/test_macros.h"
namespace TestNavigationServer3D {
+
+// TODO: Find a more generic way to create `Callable` mocks.
+class CallableMock : public Object {
+ GDCLASS(CallableMock, Object);
+
+public:
+ void function1(Variant arg0) {
+ function1_calls++;
+ function1_latest_arg0 = arg0;
+ }
+
+ unsigned function1_calls{ 0 };
+ Variant function1_latest_arg0{};
+};
+
+static inline Array build_array() {
+ return Array();
+}
+template <typename... Targs>
+static inline Array build_array(Variant item, Targs... Fargs) {
+ Array a = build_array(Fargs...);
+ a.push_front(item);
+ return a;
+}
+
TEST_SUITE("[Navigation]") {
TEST_CASE("[NavigationServer3D] Server should be empty when initialized") {
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
@@ -56,7 +83,6 @@ TEST_SUITE("[Navigation]") {
TEST_CASE("[NavigationServer3D] Server should manage agent properly") {
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
- CHECK_EQ(navigation_server->get_maps().size(), 0);
RID agent = navigation_server->agent_create();
CHECK(agent.is_valid());
@@ -69,6 +95,7 @@ TEST_SUITE("[Navigation]") {
bool initial_use_3d_avoidance = navigation_server->agent_get_use_3d_avoidance(agent);
navigation_server->agent_set_use_3d_avoidance(agent, !initial_use_3d_avoidance);
navigation_server->process(0.0); // Give server some cycles to commit.
+
CHECK_EQ(navigation_server->agent_get_use_3d_avoidance(agent), !initial_use_3d_avoidance);
// TODO: Add remaining setters/getters once the missing getters are added.
}
@@ -83,20 +110,40 @@ TEST_SUITE("[Navigation]") {
navigation_server->agent_set_map(agent, RID());
navigation_server->free(map);
navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_AGENT_COUNT), 0);
}
navigation_server->free(agent);
-
- SUBCASE("'ProcessInfo' should not report removed agent") {
- CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_AGENT_COUNT), 0);
- }
}
TEST_CASE("[NavigationServer3D] Server should manage map properly") {
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
- CHECK_EQ(navigation_server->get_maps().size(), 0);
- RID map = navigation_server->map_create();
+ RID map;
+ CHECK_FALSE(map.is_valid());
+
+ SUBCASE("Queries against invalid map should return empty or invalid values") {
+ CHECK_EQ(navigation_server->map_get_closest_point(map, Vector3(7, 7, 7)), Vector3());
+ CHECK_EQ(navigation_server->map_get_closest_point_normal(map, Vector3(7, 7, 7)), Vector3());
+ CHECK_FALSE(navigation_server->map_get_closest_point_owner(map, Vector3(7, 7, 7)).is_valid());
+ CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(7, 7, 7), Vector3(8, 8, 8), true), Vector3());
+ CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(7, 7, 7), Vector3(8, 8, 8), false), Vector3());
+ CHECK_EQ(navigation_server->map_get_path(map, Vector3(7, 7, 7), Vector3(8, 8, 8), true).size(), 0);
+ CHECK_EQ(navigation_server->map_get_path(map, Vector3(7, 7, 7), Vector3(8, 8, 8), false).size(), 0);
+
+ Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
+ query_parameters->set_map(map);
+ query_parameters->set_start_position(Vector3(7, 7, 7));
+ query_parameters->set_target_position(Vector3(8, 8, 8));
+ Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
+ navigation_server->query_path(query_parameters, query_result);
+ CHECK_EQ(query_result->get_path().size(), 0);
+ CHECK_EQ(query_result->get_path_types().size(), 0);
+ CHECK_EQ(query_result->get_path_rids().size(), 0);
+ CHECK_EQ(query_result->get_path_owner_ids().size(), 0);
+ }
+
+ map = navigation_server->map_create();
CHECK(map.is_valid());
CHECK_EQ(navigation_server->get_maps().size(), 1);
@@ -112,6 +159,7 @@ TEST_SUITE("[Navigation]") {
bool initial_use_edge_connections = navigation_server->map_get_use_edge_connections(map);
navigation_server->map_set_use_edge_connections(map, !initial_use_edge_connections);
navigation_server->process(0.0); // Give server some cycles to commit.
+
CHECK_EQ(navigation_server->map_get_cell_size(map), doctest::Approx(0.55));
CHECK_EQ(navigation_server->map_get_edge_connection_margin(map), doctest::Approx(0.66));
CHECK_EQ(navigation_server->map_get_link_connection_radius(map), doctest::Approx(0.77));
@@ -140,10 +188,429 @@ TEST_SUITE("[Navigation]") {
CHECK_EQ(navigation_server->map_get_agents(map).size(), 0);
}
+ SUBCASE("Number of links should be reported properly") {
+ RID link = navigation_server->link_create();
+ CHECK(link.is_valid());
+ navigation_server->link_set_map(link, map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->map_get_links(map).size(), 1);
+ navigation_server->free(link);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->map_get_links(map).size(), 0);
+ }
+
+ SUBCASE("Number of obstacles should be reported properly") {
+ RID obstacle = navigation_server->obstacle_create();
+ CHECK(obstacle.is_valid());
+ navigation_server->obstacle_set_map(obstacle, map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->map_get_obstacles(map).size(), 1);
+ navigation_server->free(obstacle);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->map_get_obstacles(map).size(), 0);
+ }
+
+ SUBCASE("Number of regions should be reported properly") {
+ RID region = navigation_server->region_create();
+ CHECK(region.is_valid());
+ navigation_server->region_set_map(region, map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->map_get_regions(map).size(), 1);
+ navigation_server->free(region);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->map_get_regions(map).size(), 0);
+ }
+
+ SUBCASE("Queries against empty map should return empty or invalid values") {
+ navigation_server->map_set_active(map, true);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+
+ CHECK_EQ(navigation_server->map_get_closest_point(map, Vector3(7, 7, 7)), Vector3());
+ CHECK_EQ(navigation_server->map_get_closest_point_normal(map, Vector3(7, 7, 7)), Vector3());
+ CHECK_FALSE(navigation_server->map_get_closest_point_owner(map, Vector3(7, 7, 7)).is_valid());
+ CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(7, 7, 7), Vector3(8, 8, 8), true), Vector3());
+ CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(7, 7, 7), Vector3(8, 8, 8), false), Vector3());
+ CHECK_EQ(navigation_server->map_get_path(map, Vector3(7, 7, 7), Vector3(8, 8, 8), true).size(), 0);
+ CHECK_EQ(navigation_server->map_get_path(map, Vector3(7, 7, 7), Vector3(8, 8, 8), false).size(), 0);
+
+ Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
+ query_parameters->set_map(map);
+ query_parameters->set_start_position(Vector3(7, 7, 7));
+ query_parameters->set_target_position(Vector3(8, 8, 8));
+ Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
+ navigation_server->query_path(query_parameters, query_result);
+ CHECK_EQ(query_result->get_path().size(), 0);
+ CHECK_EQ(query_result->get_path_types().size(), 0);
+ CHECK_EQ(query_result->get_path_rids().size(), 0);
+ CHECK_EQ(query_result->get_path_owner_ids().size(), 0);
+
+ navigation_server->map_set_active(map, false);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ }
+
navigation_server->free(map);
navigation_server->process(0.0); // Give server some cycles to actually remove map.
CHECK_EQ(navigation_server->get_maps().size(), 0);
}
+
+ TEST_CASE("[NavigationServer3D] Server should manage link properly") {
+ NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
+
+ RID link = navigation_server->link_create();
+ CHECK(link.is_valid());
+
+ SUBCASE("'ProcessInfo' should not report dangling link") {
+ CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_LINK_COUNT), 0);
+ }
+
+ SUBCASE("Setters/getters should work") {
+ bool initial_bidirectional = navigation_server->link_is_bidirectional(link);
+ navigation_server->link_set_bidirectional(link, !initial_bidirectional);
+ navigation_server->link_set_end_position(link, Vector3(7, 7, 7));
+ navigation_server->link_set_enter_cost(link, 0.55);
+ navigation_server->link_set_navigation_layers(link, 6);
+ navigation_server->link_set_owner_id(link, ObjectID((int64_t)7));
+ navigation_server->link_set_start_position(link, Vector3(8, 8, 8));
+ navigation_server->link_set_travel_cost(link, 0.66);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+
+ CHECK_EQ(navigation_server->link_is_bidirectional(link), !initial_bidirectional);
+ CHECK_EQ(navigation_server->link_get_end_position(link), Vector3(7, 7, 7));
+ CHECK_EQ(navigation_server->link_get_enter_cost(link), doctest::Approx(0.55));
+ CHECK_EQ(navigation_server->link_get_navigation_layers(link), 6);
+ CHECK_EQ(navigation_server->link_get_owner_id(link), ObjectID((int64_t)7));
+ CHECK_EQ(navigation_server->link_get_start_position(link), Vector3(8, 8, 8));
+ CHECK_EQ(navigation_server->link_get_travel_cost(link), doctest::Approx(0.66));
+ }
+
+ SUBCASE("'ProcessInfo' should report link with active map") {
+ RID map = navigation_server->map_create();
+ CHECK(map.is_valid());
+ navigation_server->map_set_active(map, true);
+ navigation_server->link_set_map(link, map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_LINK_COUNT), 1);
+ navigation_server->link_set_map(link, RID());
+ navigation_server->free(map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_LINK_COUNT), 0);
+ }
+
+ navigation_server->free(link);
+ }
+
+ TEST_CASE("[NavigationServer3D] Server should manage obstacles properly") {
+ NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
+
+ RID obstacle = navigation_server->obstacle_create();
+ CHECK(obstacle.is_valid());
+
+ // TODO: Add tests for setters/getters once getters are added.
+
+ navigation_server->free(obstacle);
+ }
+
+ TEST_CASE("[NavigationServer3D] Server should manage regions properly") {
+ NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
+
+ RID region = navigation_server->region_create();
+ CHECK(region.is_valid());
+
+ SUBCASE("'ProcessInfo' should not report dangling region") {
+ CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_REGION_COUNT), 0);
+ }
+
+ SUBCASE("Setters/getters should work") {
+ bool initial_use_edge_connections = navigation_server->region_get_use_edge_connections(region);
+ navigation_server->region_set_enter_cost(region, 0.55);
+ navigation_server->region_set_navigation_layers(region, 5);
+ navigation_server->region_set_owner_id(region, ObjectID((int64_t)7));
+ navigation_server->region_set_travel_cost(region, 0.66);
+ navigation_server->region_set_use_edge_connections(region, !initial_use_edge_connections);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+
+ CHECK_EQ(navigation_server->region_get_enter_cost(region), doctest::Approx(0.55));
+ CHECK_EQ(navigation_server->region_get_navigation_layers(region), 5);
+ CHECK_EQ(navigation_server->region_get_owner_id(region), ObjectID((int64_t)7));
+ CHECK_EQ(navigation_server->region_get_travel_cost(region), doctest::Approx(0.66));
+ CHECK_EQ(navigation_server->region_get_use_edge_connections(region), !initial_use_edge_connections);
+ }
+
+ SUBCASE("'ProcessInfo' should report region with active map") {
+ RID map = navigation_server->map_create();
+ CHECK(map.is_valid());
+ navigation_server->map_set_active(map, true);
+ navigation_server->region_set_map(region, map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_REGION_COUNT), 1);
+ navigation_server->region_set_map(region, RID());
+ navigation_server->free(map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(navigation_server->get_process_info(NavigationServer3D::INFO_REGION_COUNT), 0);
+ }
+
+ SUBCASE("Queries against empty region should return empty or invalid values") {
+ CHECK_EQ(navigation_server->region_get_connections_count(region), 0);
+ CHECK_EQ(navigation_server->region_get_connection_pathway_end(region, 55), Vector3());
+ CHECK_EQ(navigation_server->region_get_connection_pathway_start(region, 55), Vector3());
+ }
+
+ navigation_server->free(region);
+ }
+
+ // This test case does not check precise values on purpose - to not be too sensitivte.
+ TEST_CASE("[NavigationServer3D] Server should move agent properly") {
+ NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
+
+ RID map = navigation_server->map_create();
+ RID agent = navigation_server->agent_create();
+
+ navigation_server->map_set_active(map, true);
+ navigation_server->agent_set_map(agent, map);
+ navigation_server->agent_set_avoidance_enabled(agent, true);
+ navigation_server->agent_set_velocity(agent, Vector3(1, 0, 1));
+ CallableMock agent_avoidance_callback_mock;
+ navigation_server->agent_set_avoidance_callback(agent, callable_mp(&agent_avoidance_callback_mock, &CallableMock::function1));
+ CHECK_EQ(agent_avoidance_callback_mock.function1_calls, 0);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(agent_avoidance_callback_mock.function1_calls, 1);
+ CHECK_NE(agent_avoidance_callback_mock.function1_latest_arg0, Vector3(0, 0, 0));
+
+ navigation_server->free(agent);
+ navigation_server->free(map);
+ }
+
+ // This test case does not check precise values on purpose - to not be too sensitivte.
+ TEST_CASE("[NavigationServer3D] Server should make agents avoid each other when avoidance enabled") {
+ NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
+
+ RID map = navigation_server->map_create();
+ RID agent_1 = navigation_server->agent_create();
+ RID agent_2 = navigation_server->agent_create();
+
+ navigation_server->map_set_active(map, true);
+
+ navigation_server->agent_set_map(agent_1, map);
+ navigation_server->agent_set_avoidance_enabled(agent_1, true);
+ navigation_server->agent_set_position(agent_1, Vector3(0, 0, 0));
+ navigation_server->agent_set_radius(agent_1, 1);
+ navigation_server->agent_set_velocity(agent_1, Vector3(1, 0, 0));
+ CallableMock agent_1_avoidance_callback_mock;
+ navigation_server->agent_set_avoidance_callback(agent_1, callable_mp(&agent_1_avoidance_callback_mock, &CallableMock::function1));
+
+ navigation_server->agent_set_map(agent_2, map);
+ navigation_server->agent_set_avoidance_enabled(agent_2, true);
+ navigation_server->agent_set_position(agent_2, Vector3(2.5, 0, 0.5));
+ navigation_server->agent_set_radius(agent_2, 1);
+ navigation_server->agent_set_velocity(agent_2, Vector3(-1, 0, 0));
+ CallableMock agent_2_avoidance_callback_mock;
+ navigation_server->agent_set_avoidance_callback(agent_2, callable_mp(&agent_2_avoidance_callback_mock, &CallableMock::function1));
+
+ CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 0);
+ CHECK_EQ(agent_2_avoidance_callback_mock.function1_calls, 0);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 1);
+ CHECK_EQ(agent_2_avoidance_callback_mock.function1_calls, 1);
+ Vector3 agent_1_safe_velocity = agent_1_avoidance_callback_mock.function1_latest_arg0;
+ Vector3 agent_2_safe_velocity = agent_2_avoidance_callback_mock.function1_latest_arg0;
+ CHECK_MESSAGE(agent_1_safe_velocity.x > 0, "agent 1 should move a bit along desired velocity (+X)");
+ CHECK_MESSAGE(agent_2_safe_velocity.x < 0, "agent 2 should move a bit along desired velocity (-X)");
+ CHECK_MESSAGE(agent_1_safe_velocity.z < 0, "agent 1 should move a bit to the side so that it avoids agent 2");
+ CHECK_MESSAGE(agent_2_safe_velocity.z > 0, "agent 2 should move a bit to the side so that it avoids agent 1");
+
+ navigation_server->free(agent_2);
+ navigation_server->free(agent_1);
+ navigation_server->free(map);
+ }
+
+ // This test case uses only public APIs on purpose - other test cases use simplified baking.
+ // FIXME: Remove once deprecated `region_bake_navigation_mesh()` is removed.
+ TEST_CASE("[NavigationServer3D][SceneTree][DEPRECATED] Server should be able to bake map correctly") {
+ NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
+
+ // Prepare scene tree with simple mesh to serve as an input geometry.
+ Node3D *node_3d = memnew(Node3D);
+ SceneTree::get_singleton()->get_root()->add_child(node_3d);
+ Ref<PlaneMesh> plane_mesh = memnew(PlaneMesh);
+ plane_mesh->set_size(Size2(10.0, 10.0));
+ MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
+ mesh_instance->set_mesh(plane_mesh);
+ node_3d->add_child(mesh_instance);
+
+ // Prepare anything necessary to bake navigation mesh.
+ RID map = navigation_server->map_create();
+ RID region = navigation_server->region_create();
+ Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
+ navigation_server->map_set_active(map, true);
+ navigation_server->region_set_map(region, map);
+ navigation_server->region_set_navigation_mesh(region, navigation_mesh);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+
+ CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
+ CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
+
+ navigation_server->region_bake_navigation_mesh(navigation_mesh, node_3d);
+ // FIXME: The above line should trigger the update (line below) under the hood.
+ navigation_server->region_set_navigation_mesh(region, navigation_mesh); // Force update.
+ CHECK_EQ(navigation_mesh->get_polygon_count(), 2);
+ CHECK_EQ(navigation_mesh->get_vertices().size(), 4);
+
+ SUBCASE("Map should emit signal and take newly baked navigation mesh into account") {
+ SIGNAL_WATCH(navigation_server, "map_changed");
+ SIGNAL_CHECK_FALSE("map_changed");
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ SIGNAL_CHECK("map_changed", build_array(build_array(map)));
+ SIGNAL_UNWATCH(navigation_server, "map_changed");
+ CHECK_NE(navigation_server->map_get_closest_point(map, Vector3(0, 0, 0)), Vector3(0, 0, 0));
+ }
+
+ navigation_server->free(region);
+ navigation_server->free(map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ memdelete(mesh_instance);
+ memdelete(node_3d);
+ }
+
+ // This test case uses only public APIs on purpose - other test cases use simplified baking.
+ TEST_CASE("[NavigationServer3D][SceneTree] Server should be able to bake map correctly") {
+ NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
+
+ // Prepare scene tree with simple mesh to serve as an input geometry.
+ Node3D *node_3d = memnew(Node3D);
+ SceneTree::get_singleton()->get_root()->add_child(node_3d);
+ Ref<PlaneMesh> plane_mesh = memnew(PlaneMesh);
+ plane_mesh->set_size(Size2(10.0, 10.0));
+ MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
+ mesh_instance->set_mesh(plane_mesh);
+ node_3d->add_child(mesh_instance);
+
+ // Prepare anything necessary to bake navigation mesh.
+ RID map = navigation_server->map_create();
+ RID region = navigation_server->region_create();
+ Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
+ navigation_server->map_set_active(map, true);
+ navigation_server->region_set_map(region, map);
+ navigation_server->region_set_navigation_mesh(region, navigation_mesh);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+
+ CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
+ CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
+
+ Ref<NavigationMeshSourceGeometryData3D> source_geometry = memnew(NavigationMeshSourceGeometryData3D);
+ navigation_server->parse_source_geometry_data(navigation_mesh, source_geometry, node_3d);
+ navigation_server->bake_from_source_geometry_data(navigation_mesh, source_geometry, Callable());
+ // FIXME: The above line should trigger the update (line below) under the hood.
+ navigation_server->region_set_navigation_mesh(region, navigation_mesh); // Force update.
+ CHECK_EQ(navigation_mesh->get_polygon_count(), 2);
+ CHECK_EQ(navigation_mesh->get_vertices().size(), 4);
+
+ SUBCASE("Map should emit signal and take newly baked navigation mesh into account") {
+ SIGNAL_WATCH(navigation_server, "map_changed");
+ SIGNAL_CHECK_FALSE("map_changed");
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ SIGNAL_CHECK("map_changed", build_array(build_array(map)));
+ SIGNAL_UNWATCH(navigation_server, "map_changed");
+ CHECK_NE(navigation_server->map_get_closest_point(map, Vector3(0, 0, 0)), Vector3(0, 0, 0));
+ }
+
+ navigation_server->free(region);
+ navigation_server->free(map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ memdelete(mesh_instance);
+ memdelete(node_3d);
+ }
+
+ // This test case does not check precise values on purpose - to not be too sensitivte.
+ TEST_CASE("[NavigationServer3D] Server should respond to queries against valid map properly") {
+ NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
+ Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
+ Ref<NavigationMeshSourceGeometryData3D> source_geometry = memnew(NavigationMeshSourceGeometryData3D);
+
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ BoxMesh::create_mesh_array(arr, Vector3(10.0, 0.001, 10.0));
+ source_geometry->add_mesh_array(arr, Transform3D());
+ navigation_server->bake_from_source_geometry_data(navigation_mesh, source_geometry, Callable());
+ CHECK_NE(navigation_mesh->get_polygon_count(), 0);
+ CHECK_NE(navigation_mesh->get_vertices().size(), 0);
+
+ RID map = navigation_server->map_create();
+ RID region = navigation_server->region_create();
+ navigation_server->map_set_active(map, true);
+ navigation_server->region_set_map(region, map);
+ navigation_server->region_set_navigation_mesh(region, navigation_mesh);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+
+ SUBCASE("Simple queries should return non-default values") {
+ CHECK_NE(navigation_server->map_get_closest_point(map, Vector3(0, 0, 0)), Vector3(0, 0, 0));
+ CHECK_NE(navigation_server->map_get_closest_point_normal(map, Vector3(0, 0, 0)), Vector3());
+ CHECK(navigation_server->map_get_closest_point_owner(map, Vector3(0, 0, 0)).is_valid());
+ // TODO: Test map_get_closest_point_to_segment() with p_use_collision=true as well.
+ CHECK_NE(navigation_server->map_get_closest_point_to_segment(map, Vector3(0, 0, 0), Vector3(1, 1, 1), false), Vector3());
+ CHECK_NE(navigation_server->map_get_path(map, Vector3(0, 0, 0), Vector3(10, 0, 10), true).size(), 0);
+ CHECK_NE(navigation_server->map_get_path(map, Vector3(0, 0, 0), Vector3(10, 0, 10), false).size(), 0);
+ }
+
+ SUBCASE("Elaborate query with 'CORRIDORFUNNEL' post-processing should yield non-empty result") {
+ Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
+ query_parameters->set_map(map);
+ query_parameters->set_start_position(Vector3(0, 0, 0));
+ query_parameters->set_target_position(Vector3(10, 0, 10));
+ query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PATH_POSTPROCESSING_CORRIDORFUNNEL);
+ Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
+ navigation_server->query_path(query_parameters, query_result);
+ CHECK_NE(query_result->get_path().size(), 0);
+ CHECK_NE(query_result->get_path_types().size(), 0);
+ CHECK_NE(query_result->get_path_rids().size(), 0);
+ CHECK_NE(query_result->get_path_owner_ids().size(), 0);
+ }
+
+ SUBCASE("Elaborate query with 'EDGECENTERED' post-processing should yield non-empty result") {
+ Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
+ query_parameters->set_map(map);
+ query_parameters->set_start_position(Vector3(10, 0, 10));
+ query_parameters->set_target_position(Vector3(0, 0, 0));
+ query_parameters->set_path_postprocessing(NavigationPathQueryParameters3D::PATH_POSTPROCESSING_EDGECENTERED);
+ Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
+ navigation_server->query_path(query_parameters, query_result);
+ CHECK_NE(query_result->get_path().size(), 0);
+ CHECK_NE(query_result->get_path_types().size(), 0);
+ CHECK_NE(query_result->get_path_rids().size(), 0);
+ CHECK_NE(query_result->get_path_owner_ids().size(), 0);
+ }
+
+ SUBCASE("Elaborate query with non-matching navigation layer mask should yield empty result") {
+ Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
+ query_parameters->set_map(map);
+ query_parameters->set_start_position(Vector3(10, 0, 10));
+ query_parameters->set_target_position(Vector3(0, 0, 0));
+ query_parameters->set_navigation_layers(2);
+ Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
+ navigation_server->query_path(query_parameters, query_result);
+ CHECK_EQ(query_result->get_path().size(), 0);
+ CHECK_EQ(query_result->get_path_types().size(), 0);
+ CHECK_EQ(query_result->get_path_rids().size(), 0);
+ CHECK_EQ(query_result->get_path_owner_ids().size(), 0);
+ }
+
+ SUBCASE("Elaborate query without metadata flags should yield path only") {
+ Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
+ query_parameters->set_map(map);
+ query_parameters->set_start_position(Vector3(10, 0, 10));
+ query_parameters->set_target_position(Vector3(0, 0, 0));
+ query_parameters->set_metadata_flags(0);
+ Ref<NavigationPathQueryResult3D> query_result = memnew(NavigationPathQueryResult3D);
+ navigation_server->query_path(query_parameters, query_result);
+ CHECK_NE(query_result->get_path().size(), 0);
+ CHECK_EQ(query_result->get_path_types().size(), 0);
+ CHECK_EQ(query_result->get_path_rids().size(), 0);
+ CHECK_EQ(query_result->get_path_owner_ids().size(), 0);
+ }
+
+ navigation_server->free(region);
+ navigation_server->free(map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ }
}
} //namespace TestNavigationServer3D
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 071dea330c..a918acbe77 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -115,7 +115,7 @@ commits.
## enet
- Upstream: http://enet.bespin.org
-- Version: 1.3.17 (e0e7045b7e056b454b5093cb34df49dc4cee0bee, 2020)
+- Version: git (ea4607a90dbfbcf4da2669ea998585253d8e70b1, 2023)
- License: MIT
Files extracted from upstream source:
diff --git a/thirdparty/enet/enet/enet.h b/thirdparty/enet/enet/enet.h
index 77f8004b80..5232f8a869 100644
--- a/thirdparty/enet/enet/enet.h
+++ b/thirdparty/enet/enet/enet.h
@@ -68,7 +68,8 @@ typedef enum _ENetSocketOption
ENET_SOCKOPT_RCVTIMEO = 6,
ENET_SOCKOPT_SNDTIMEO = 7,
ENET_SOCKOPT_ERROR = 8,
- ENET_SOCKOPT_NODELAY = 9
+ ENET_SOCKOPT_NODELAY = 9,
+ ENET_SOCKOPT_TTL = 10
} ENetSocketOption;
typedef enum _ENetSocketShutdown
@@ -179,7 +180,7 @@ typedef struct _ENetOutgoingCommand
enet_uint16 unreliableSequenceNumber;
enet_uint32 sentTime;
enet_uint32 roundTripTimeout;
- enet_uint32 roundTripTimeoutLimit;
+ enet_uint32 queueTime;
enet_uint32 fragmentOffset;
enet_uint16 fragmentLength;
enet_uint16 sendAttempts;
@@ -222,7 +223,7 @@ enum
ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024,
ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024,
ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000,
- ENET_HOST_DEFAULT_MTU = 1400,
+ ENET_HOST_DEFAULT_MTU = 1392,
ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024,
ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024,
@@ -262,7 +263,8 @@ typedef struct _ENetChannel
typedef enum _ENetPeerFlag
{
- ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0)
+ ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0),
+ ENET_PEER_FLAG_CONTINUE_SENDING = (1 << 1)
} ENetPeerFlag;
/**
@@ -322,7 +324,7 @@ typedef struct _ENetPeer
enet_uint16 outgoingReliableSequenceNumber;
ENetList acknowledgements;
ENetList sentReliableCommands;
- ENetList sentUnreliableCommands;
+ ENetList outgoingSendReliableCommands;
ENetList outgoingCommands;
ENetList dispatchedCommands;
enet_uint16 flags;
@@ -385,7 +387,7 @@ typedef struct _ENetHost
size_t channelLimit; /**< maximum number of channels allowed for connected peers */
enet_uint32 serviceTime;
ENetList dispatchQueue;
- int continueSending;
+ enet_uint32 totalQueued;
size_t packetSize;
enet_uint16 headerFlags;
ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS];
@@ -585,6 +587,7 @@ ENET_API void enet_host_channel_limit (ENetHost *, size_t);
ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32);
extern void enet_host_bandwidth_throttle (ENetHost *);
extern enet_uint32 enet_host_random_seed (void);
+extern enet_uint32 enet_host_random (ENetHost *);
ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *);
ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID);
@@ -598,6 +601,7 @@ ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32
ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
extern int enet_peer_throttle (ENetPeer *, enet_uint32);
extern void enet_peer_reset_queues (ENetPeer *);
+extern int enet_peer_has_outgoing_commands (ENetPeer *);
extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *);
extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16);
extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32);
diff --git a/thirdparty/enet/godot.cpp b/thirdparty/enet/godot.cpp
index 2cbfe59fc6..9ce126a475 100644
--- a/thirdparty/enet/godot.cpp
+++ b/thirdparty/enet/godot.cpp
@@ -535,6 +535,10 @@ int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buf
if (err == ERR_BUSY) {
return 0;
}
+ if (err == ERR_OUT_OF_MEMORY) {
+ // A packet above the ENET_PROTOCOL_MAXIMUM_MTU was received.
+ return -2;
+ }
if (err != OK) {
return -1;
diff --git a/thirdparty/enet/host.c b/thirdparty/enet/host.c
index 21ab27e247..adb3533cf1 100644
--- a/thirdparty/enet/host.c
+++ b/thirdparty/enet/host.c
@@ -96,6 +96,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL
host -> totalSentPackets = 0;
host -> totalReceivedData = 0;
host -> totalReceivedPackets = 0;
+ host -> totalQueued = 0;
host -> connectedPeers = 0;
host -> bandwidthLimitedPeers = 0;
@@ -123,8 +124,8 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL
enet_list_clear (& currentPeer -> acknowledgements);
enet_list_clear (& currentPeer -> sentReliableCommands);
- enet_list_clear (& currentPeer -> sentUnreliableCommands);
enet_list_clear (& currentPeer -> outgoingCommands);
+ enet_list_clear (& currentPeer -> outgoingSendReliableCommands);
enet_list_clear (& currentPeer -> dispatchedCommands);
enet_peer_reset (currentPeer);
@@ -160,6 +161,16 @@ enet_host_destroy (ENetHost * host)
enet_free (host);
}
+enet_uint32
+enet_host_random (ENetHost * host)
+{
+ /* Mulberry32 by Tommy Ettinger */
+ enet_uint32 n = (host -> randomSeed += 0x6D2B79F5U);
+ n = (n ^ (n >> 15)) * (n | 1U);
+ n ^= n + (n ^ (n >> 7)) * (n | 61U);
+ return n ^ (n >> 14);
+}
+
/** Initiates a connection to a foreign host.
@param host host seeking the connection
@param address destination for the connection
@@ -199,7 +210,8 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC
currentPeer -> channelCount = channelCount;
currentPeer -> state = ENET_PEER_STATE_CONNECTING;
currentPeer -> address = * address;
- currentPeer -> connectID = ++ host -> randomSeed;
+ currentPeer -> connectID = enet_host_random (host);
+ currentPeer -> mtu = host -> mtu;
if (host -> outgoingBandwidth == 0)
currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
diff --git a/thirdparty/enet/packet.c b/thirdparty/enet/packet.c
index 5fa78b28ae..d51c640404 100644
--- a/thirdparty/enet/packet.c
+++ b/thirdparty/enet/packet.c
@@ -98,53 +98,46 @@ enet_packet_resize (ENetPacket * packet, size_t dataLength)
return 0;
}
-static int initializedCRC32 = 0;
-static enet_uint32 crcTable [256];
-
-static enet_uint32
-reflect_crc (int val, int bits)
+static const enet_uint32 crcTable [256] =
{
- int result = 0, bit;
-
- for (bit = 0; bit < bits; bit ++)
- {
- if(val & 1) result |= 1 << (bits - 1 - bit);
- val >>= 1;
- }
-
- return result;
-}
-
-static void
-initialize_crc32 (void)
-{
- int byte;
-
- for (byte = 0; byte < 256; ++ byte)
- {
- enet_uint32 crc = reflect_crc (byte, 8) << 24;
- int offset;
+ 0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+ 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+ 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+ 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+ 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+ 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+ 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+ 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+ 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+ 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+ 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+ 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+ 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+ 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+ 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x5005713,
+ 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0xBDBDF21,
+ 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+ 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+ 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+};
- for(offset = 0; offset < 8; ++ offset)
- {
- if (crc & 0x80000000)
- crc = (crc << 1) ^ 0x04c11db7;
- else
- crc <<= 1;
- }
-
- crcTable [byte] = reflect_crc (crc, 32);
- }
-
- initializedCRC32 = 1;
-}
-
enet_uint32
enet_crc32 (const ENetBuffer * buffers, size_t bufferCount)
{
enet_uint32 crc = 0xFFFFFFFF;
-
- if (! initializedCRC32) initialize_crc32 ();
while (bufferCount -- > 0)
{
@@ -153,7 +146,7 @@ enet_crc32 (const ENetBuffer * buffers, size_t bufferCount)
while (data < dataEnd)
{
- crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++];
+ crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++];
}
++ buffers;
diff --git a/thirdparty/enet/peer.c b/thirdparty/enet/peer.c
index 9370ef4be1..a7ac012079 100644
--- a/thirdparty/enet/peer.c
+++ b/thirdparty/enet/peer.c
@@ -90,6 +90,13 @@ enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
}
/** Queues a packet to be sent.
+
+ On success, ENet will assume ownership of the packet, and so enet_packet_destroy
+ should not be called on it thereafter. On failure, the caller still must destroy
+ the packet on its own as ENet has not queued the packet. The caller can also
+ check the packet's referenceCount field after sending to check if ENet queued
+ the packet and thus incremented the referenceCount.
+
@param peer destination for the packet
@param channelID channel on which to send
@param packet packet to send
@@ -99,7 +106,7 @@ enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
int
enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
{
- ENetChannel * channel = & peer -> channels [channelID];
+ ENetChannel * channel;
ENetProtocol command;
size_t fragmentLength;
@@ -108,6 +115,7 @@ enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
packet -> dataLength > peer -> host -> maximumPacketSize)
return -1;
+ channel = & peer -> channels [channelID];
fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment);
if (peer -> host -> checksum != NULL)
fragmentLength -= sizeof(enet_uint32);
@@ -320,8 +328,8 @@ enet_peer_reset_queues (ENetPeer * peer)
enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements)));
enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands);
- enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands);
enet_peer_reset_outgoing_commands (& peer -> outgoingCommands);
+ enet_peer_reset_outgoing_commands (& peer -> outgoingSendReliableCommands);
enet_peer_reset_incoming_commands (& peer -> dispatchedCommands);
if (peer -> channels != NULL && peer -> channelCount > 0)
@@ -563,6 +571,17 @@ enet_peer_disconnect (ENetPeer * peer, enet_uint32 data)
}
}
+int
+enet_peer_has_outgoing_commands (ENetPeer * peer)
+{
+ if (enet_list_empty (& peer -> outgoingCommands) &&
+ enet_list_empty (& peer -> outgoingSendReliableCommands) &&
+ enet_list_empty (& peer -> sentReliableCommands))
+ return 0;
+
+ return 1;
+}
+
/** Request a disconnection from a peer, but only after all queued outgoing packets are sent.
@param peer peer to request a disconnection
@param data data describing the disconnection
@@ -573,8 +592,7 @@ void
enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data)
{
if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) &&
- ! (enet_list_empty (& peer -> outgoingCommands) &&
- enet_list_empty (& peer -> sentReliableCommands)))
+ enet_peer_has_outgoing_commands (peer))
{
peer -> state = ENET_PEER_STATE_DISCONNECT_LATER;
peer -> eventData = data;
@@ -618,8 +636,6 @@ enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command,
void
enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand)
{
- ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID];
-
peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength;
if (outgoingCommand -> command.header.channelID == 0xFF)
@@ -630,36 +646,40 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin
outgoingCommand -> unreliableSequenceNumber = 0;
}
else
- if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
{
- ++ channel -> outgoingReliableSequenceNumber;
- channel -> outgoingUnreliableSequenceNumber = 0;
+ ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID];
- outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
- outgoingCommand -> unreliableSequenceNumber = 0;
- }
- else
- if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED)
- {
- ++ peer -> outgoingUnsequencedGroup;
+ if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
+ {
+ ++ channel -> outgoingReliableSequenceNumber;
+ channel -> outgoingUnreliableSequenceNumber = 0;
- outgoingCommand -> reliableSequenceNumber = 0;
- outgoingCommand -> unreliableSequenceNumber = 0;
- }
- else
- {
- if (outgoingCommand -> fragmentOffset == 0)
- ++ channel -> outgoingUnreliableSequenceNumber;
-
- outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
- outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber;
+ outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+ outgoingCommand -> unreliableSequenceNumber = 0;
+ }
+ else
+ if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED)
+ {
+ ++ peer -> outgoingUnsequencedGroup;
+
+ outgoingCommand -> reliableSequenceNumber = 0;
+ outgoingCommand -> unreliableSequenceNumber = 0;
+ }
+ else
+ {
+ if (outgoingCommand -> fragmentOffset == 0)
+ ++ channel -> outgoingUnreliableSequenceNumber;
+
+ outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+ outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber;
+ }
}
-
+
outgoingCommand -> sendAttempts = 0;
outgoingCommand -> sentTime = 0;
outgoingCommand -> roundTripTimeout = 0;
- outgoingCommand -> roundTripTimeoutLimit = 0;
outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber);
+ outgoingCommand -> queueTime = ++ peer -> host -> totalQueued;
switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK)
{
@@ -670,12 +690,16 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin
case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup);
break;
-
+
default:
break;
}
- enet_list_insert (enet_list_end (& peer -> outgoingCommands), outgoingCommand);
+ if ((outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0 &&
+ outgoingCommand -> packet != NULL)
+ enet_list_insert (enet_list_end (& peer -> outgoingSendReliableCommands), outgoingCommand);
+ else
+ enet_list_insert (enet_list_end (& peer -> outgoingCommands), outgoingCommand);
}
ENetOutgoingCommand *
diff --git a/thirdparty/enet/protocol.c b/thirdparty/enet/protocol.c
index d7fe80f117..af307af7e5 100644
--- a/thirdparty/enet/protocol.c
+++ b/thirdparty/enet/protocol.c
@@ -9,7 +9,7 @@
#include "enet/time.h"
#include "enet/enet.h"
-static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] =
+static const size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] =
{
0,
sizeof (ENetProtocolAcknowledge),
@@ -159,16 +159,16 @@ enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * e
}
static void
-enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer)
+enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer, ENetList * sentUnreliableCommands)
{
ENetOutgoingCommand * outgoingCommand;
- if (enet_list_empty (& peer -> sentUnreliableCommands))
+ if (enet_list_empty (sentUnreliableCommands))
return;
do
{
- outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands);
+ outgoingCommand = (ENetOutgoingCommand *) enet_list_front (sentUnreliableCommands);
enet_list_remove (& outgoingCommand -> outgoingCommandList);
@@ -185,14 +185,38 @@ enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer)
}
enet_free (outgoingCommand);
- } while (! enet_list_empty (& peer -> sentUnreliableCommands));
+ } while (! enet_list_empty (sentUnreliableCommands));
if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER &&
- enet_list_empty (& peer -> outgoingCommands) &&
- enet_list_empty (& peer -> sentReliableCommands))
+ ! enet_peer_has_outgoing_commands (peer))
enet_peer_disconnect (peer, peer -> eventData);
}
+static ENetOutgoingCommand *
+enet_protocol_find_sent_reliable_command (ENetList * list, enet_uint16 reliableSequenceNumber, enet_uint8 channelID)
+{
+ ENetListIterator currentCommand;
+
+ for (currentCommand = enet_list_begin (list);
+ currentCommand != enet_list_end (list);
+ currentCommand = enet_list_next (currentCommand))
+ {
+ ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+ if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE))
+ continue;
+
+ if (outgoingCommand -> sendAttempts < 1)
+ break;
+
+ if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
+ outgoingCommand -> command.header.channelID == channelID)
+ return outgoingCommand;
+ }
+
+ return NULL;
+}
+
static ENetProtocolCommand
enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID)
{
@@ -214,24 +238,9 @@ enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliabl
if (currentCommand == enet_list_end (& peer -> sentReliableCommands))
{
- for (currentCommand = enet_list_begin (& peer -> outgoingCommands);
- currentCommand != enet_list_end (& peer -> outgoingCommands);
- currentCommand = enet_list_next (currentCommand))
- {
- outgoingCommand = (ENetOutgoingCommand *) currentCommand;
-
- if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE))
- continue;
-
- if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE;
-
- if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
- outgoingCommand -> command.header.channelID == channelID)
- break;
- }
-
- if (currentCommand == enet_list_end (& peer -> outgoingCommands))
- return ENET_PROTOCOL_COMMAND_NONE;
+ outgoingCommand = enet_protocol_find_sent_reliable_command (& peer -> outgoingCommands, reliableSequenceNumber, channelID);
+ if (outgoingCommand == NULL)
+ outgoingCommand = enet_protocol_find_sent_reliable_command (& peer -> outgoingSendReliableCommands, reliableSequenceNumber, channelID);
wasSent = 0;
}
@@ -331,6 +340,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet
peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT;
peer -> connectID = command -> connect.connectID;
peer -> address = host -> receivedAddress;
+ peer -> mtu = host -> mtu;
peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID);
peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth);
peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth);
@@ -375,7 +385,8 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet
if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
mtu = ENET_PROTOCOL_MAXIMUM_MTU;
- peer -> mtu = mtu;
+ if (mtu < peer -> mtu)
+ peer -> mtu = mtu;
if (host -> outgoingBandwidth == 0 &&
peer -> incomingBandwidth == 0)
@@ -542,7 +553,8 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet
fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength);
* currentData += fragmentLength;
- if (fragmentLength > host -> maximumPacketSize ||
+ if (fragmentLength <= 0 ||
+ fragmentLength > host -> maximumPacketSize ||
* currentData < host -> receivedData ||
* currentData > & host -> receivedData [host -> receivedDataLength])
return -1;
@@ -566,6 +578,7 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet
if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT ||
fragmentNumber >= fragmentCount ||
totalLength > host -> maximumPacketSize ||
+ totalLength < fragmentCount ||
fragmentOffset >= totalLength ||
fragmentLength > totalLength - fragmentOffset)
return -1;
@@ -921,8 +934,7 @@ enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer *
break;
case ENET_PEER_STATE_DISCONNECT_LATER:
- if (enet_list_empty (& peer -> outgoingCommands) &&
- enet_list_empty (& peer -> sentReliableCommands))
+ if (! enet_peer_has_outgoing_commands (peer))
enet_peer_disconnect (peer, peer -> eventData);
break;
@@ -1230,6 +1242,9 @@ enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event)
& buffer,
1);
+ if (receivedLength == -2)
+ continue;
+
if (receivedLength < 0)
return -1;
@@ -1293,7 +1308,7 @@ enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer)
buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge))
{
- host -> continueSending = 1;
+ peer -> flags |= ENET_PEER_FLAG_CONTINUE_SENDING;
break;
}
@@ -1333,10 +1348,11 @@ static int
enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event)
{
ENetOutgoingCommand * outgoingCommand;
- ENetListIterator currentCommand, insertPosition;
+ ENetListIterator currentCommand, insertPosition, insertSendReliablePosition;
currentCommand = enet_list_begin (& peer -> sentReliableCommands);
insertPosition = enet_list_begin (& peer -> outgoingCommands);
+ insertSendReliablePosition = enet_list_begin (& peer -> outgoingSendReliableCommands);
while (currentCommand != enet_list_end (& peer -> sentReliableCommands))
{
@@ -1353,7 +1369,7 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even
if (peer -> earliestTimeout != 0 &&
(ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum ||
- (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit &&
+ ((1 << (outgoingCommand -> sendAttempts - 1)) >= peer -> timeoutLimit &&
ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum)))
{
enet_protocol_notify_disconnect (host, peer, event);
@@ -1361,14 +1377,18 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even
return 1;
}
- if (outgoingCommand -> packet != NULL)
- peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
-
++ peer -> packetsLost;
outgoingCommand -> roundTripTimeout *= 2;
- enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList));
+ if (outgoingCommand -> packet != NULL)
+ {
+ peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
+
+ enet_list_insert (insertSendReliablePosition, enet_list_remove (& outgoingCommand -> outgoingCommandList));
+ }
+ else
+ enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList));
if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) &&
! enet_list_empty (& peer -> sentReliableCommands))
@@ -1383,22 +1403,41 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even
}
static int
-enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
+enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer, ENetList * sentUnreliableCommands)
{
ENetProtocol * command = & host -> commands [host -> commandCount];
ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
ENetOutgoingCommand * outgoingCommand;
- ENetListIterator currentCommand;
- ENetChannel *channel;
- enet_uint16 reliableWindow;
+ ENetListIterator currentCommand, currentSendReliableCommand;
+ ENetChannel *channel = NULL;
+ enet_uint16 reliableWindow = 0;
size_t commandSize;
- int windowExceeded = 0, windowWrap = 0, canPing = 1;
+ int windowWrap = 0, canPing = 1;
currentCommand = enet_list_begin (& peer -> outgoingCommands);
-
- while (currentCommand != enet_list_end (& peer -> outgoingCommands))
+ currentSendReliableCommand = enet_list_begin (& peer -> outgoingSendReliableCommands);
+
+ for (;;)
{
- outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+ if (currentCommand != enet_list_end (& peer -> outgoingCommands))
+ {
+ outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+ if (currentSendReliableCommand != enet_list_end (& peer -> outgoingSendReliableCommands) &&
+ ENET_TIME_LESS (((ENetOutgoingCommand *) currentSendReliableCommand) -> queueTime, outgoingCommand -> queueTime))
+ goto useSendReliableCommand;
+
+ currentCommand = enet_list_next (currentCommand);
+ }
+ else
+ if (currentSendReliableCommand != enet_list_end (& peer -> outgoingSendReliableCommands))
+ {
+ useSendReliableCommand:
+ outgoingCommand = (ENetOutgoingCommand *) currentSendReliableCommand;
+ currentSendReliableCommand = enet_list_next (currentSendReliableCommand);
+ }
+ else
+ break;
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
{
@@ -1406,33 +1445,29 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
if (channel != NULL)
{
- if (! windowWrap &&
- outgoingCommand -> sendAttempts < 1 &&
+ if (windowWrap)
+ continue;
+ else
+ if (outgoingCommand -> sendAttempts < 1 &&
! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) &&
(channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE ||
channel -> usedReliableWindows & ((((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) << reliableWindow) |
(((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow)))))
- windowWrap = 1;
- if (windowWrap)
{
- currentCommand = enet_list_next (currentCommand);
-
+ windowWrap = 1;
+ currentSendReliableCommand = enet_list_end (& peer -> outgoingSendReliableCommands);
+
continue;
}
}
-
+
if (outgoingCommand -> packet != NULL)
{
- if (! windowExceeded)
- {
- enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE;
-
- if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu))
- windowExceeded = 1;
- }
- if (windowExceeded)
+ enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE;
+
+ if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu))
{
- currentCommand = enet_list_next (currentCommand);
+ currentSendReliableCommand = enet_list_end (& peer -> outgoingSendReliableCommands);
continue;
}
@@ -1448,13 +1483,11 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
(outgoingCommand -> packet != NULL &&
(enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength)))
{
- host -> continueSending = 1;
-
+ peer -> flags |= ENET_PEER_FLAG_CONTINUE_SENDING;
+
break;
}
- currentCommand = enet_list_next (currentCommand);
-
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
{
if (channel != NULL && outgoingCommand -> sendAttempts < 1)
@@ -1466,10 +1499,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
++ outgoingCommand -> sendAttempts;
if (outgoingCommand -> roundTripTimeout == 0)
- {
- outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
- outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout;
- }
+ outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
if (enet_list_empty (& peer -> sentReliableCommands))
peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout;
@@ -1522,7 +1552,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
enet_list_remove (& outgoingCommand -> outgoingCommandList);
if (outgoingCommand -> packet != NULL)
- enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand);
+ enet_list_insert (enet_list_end (sentUnreliableCommands), outgoingCommand);
}
buffer -> data = command;
@@ -1555,9 +1585,8 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
host -> bufferCount = buffer - host -> buffers;
if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER &&
- enet_list_empty (& peer -> outgoingCommands) &&
- enet_list_empty (& peer -> sentReliableCommands) &&
- enet_list_empty (& peer -> sentUnreliableCommands))
+ ! enet_peer_has_outgoing_commands (peer) &&
+ enet_list_empty (sentUnreliableCommands))
enet_peer_disconnect (peer, peer -> eventData);
return canPing;
@@ -1568,22 +1597,24 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch
{
enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)];
ENetProtocolHeader * header = (ENetProtocolHeader *) headerData;
- ENetPeer * currentPeer;
- int sentLength;
+ int sentLength = 0;
size_t shouldCompress = 0;
-
- host -> continueSending = 1;
+ ENetList sentUnreliableCommands;
- while (host -> continueSending)
- for (host -> continueSending = 0,
- currentPeer = host -> peers;
+ enet_list_clear (& sentUnreliableCommands);
+
+ for (int sendPass = 0, continueSending = 0; sendPass <= continueSending; ++ sendPass)
+ for (ENetPeer * currentPeer = host -> peers;
currentPeer < & host -> peers [host -> peerCount];
++ currentPeer)
{
if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED ||
- currentPeer -> state == ENET_PEER_STATE_ZOMBIE)
+ currentPeer -> state == ENET_PEER_STATE_ZOMBIE ||
+ (sendPass > 0 && ! (currentPeer -> flags & ENET_PEER_FLAG_CONTINUE_SENDING)))
continue;
+ currentPeer -> flags &= ~ ENET_PEER_FLAG_CONTINUE_SENDING;
+
host -> headerFlags = 0;
host -> commandCount = 0;
host -> bufferCount = 1;
@@ -1600,21 +1631,22 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch
if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
return 1;
else
- continue;
+ goto nextPeer;
}
- if ((enet_list_empty (& currentPeer -> outgoingCommands) ||
- enet_protocol_check_outgoing_commands (host, currentPeer)) &&
+ if (((enet_list_empty (& currentPeer -> outgoingCommands) &&
+ enet_list_empty (& currentPeer -> outgoingSendReliableCommands)) ||
+ enet_protocol_check_outgoing_commands (host, currentPeer, & sentUnreliableCommands)) &&
enet_list_empty (& currentPeer -> sentReliableCommands) &&
ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval &&
currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing))
{
enet_peer_ping (currentPeer);
- enet_protocol_check_outgoing_commands (host, currentPeer);
+ enet_protocol_check_outgoing_commands (host, currentPeer, & sentUnreliableCommands);
}
if (host -> commandCount == 0)
- continue;
+ goto nextPeer;
if (currentPeer -> packetLossEpoch == 0)
currentPeer -> packetLossEpoch = host -> serviceTime;
@@ -1625,7 +1657,7 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch
enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent;
#ifdef ENET_DEBUG
- printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0);
+ printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingCommands) + enet_list_size (& currentPeer -> outgoingSendReliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0);
#endif
currentPeer -> packetLossVariance = (currentPeer -> packetLossVariance * 3 + ENET_DIFFERENCE (packetLoss, currentPeer -> packetLoss)) / 4;
@@ -1687,13 +1719,17 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch
sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount);
- enet_protocol_remove_sent_unreliable_commands (currentPeer);
+ enet_protocol_remove_sent_unreliable_commands (currentPeer, & sentUnreliableCommands);
if (sentLength < 0)
return -1;
host -> totalSentData += sentLength;
host -> totalSentPackets ++;
+
+ nextPeer:
+ if (currentPeer -> flags & ENET_PEER_FLAG_CONTINUE_SENDING)
+ continueSending = sendPass + 1;
}
return 0;