summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/actions/download-artifact/action.yml18
-rw-r--r--.github/actions/godot-api-dump/action.yml24
-rw-r--r--.github/actions/godot-cache/action.yml13
-rw-r--r--.github/actions/godot-converter-test/action.yml18
-rw-r--r--.github/actions/godot-project-test/action.yml37
-rw-r--r--.github/workflows/godot_cpp_test.yml54
-rw-r--r--.github/workflows/linux_builds.yml135
-rw-r--r--.github/workflows/macos_builds.yml16
-rw-r--r--.github/workflows/runner.yml16
-rw-r--r--.github/workflows/static_checks.yml1
-rw-r--r--.github/workflows/windows_builds.yml16
-rw-r--r--CHANGELOG.md1
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--SConstruct7
-rw-r--r--core/SCsub1
-rw-r--r--core/config/project_settings.cpp2
-rw-r--r--core/core_bind.cpp10
-rw-r--r--core/core_bind.h6
-rw-r--r--core/debugger/debugger_marshalls.cpp10
-rw-r--r--core/debugger/debugger_marshalls.h1
-rw-r--r--core/debugger/engine_debugger.cpp8
-rw-r--r--core/debugger/engine_debugger.h8
-rw-r--r--core/debugger/remote_debugger.cpp109
-rw-r--r--core/debugger/remote_debugger.h11
-rw-r--r--core/debugger/script_debugger.cpp22
-rw-r--r--core/debugger/script_debugger.h23
-rw-r--r--core/doc_data.h44
-rw-r--r--core/extension/gdextension.cpp8
-rw-r--r--core/extension/gdextension.h1
-rw-r--r--core/extension/gdextension_interface.cpp6
-rw-r--r--core/extension/gdextension_interface.h36
-rw-r--r--core/input/input.cpp27
-rw-r--r--core/input/input.h4
-rw-r--r--core/io/image.cpp264
-rw-r--r--core/io/image.h7
-rw-r--r--core/io/json.cpp8
-rw-r--r--core/io/resource.cpp36
-rw-r--r--core/io/resource.h11
-rw-r--r--core/io/resource_format_binary.cpp20
-rw-r--r--core/io/resource_format_binary.h2
-rw-r--r--core/io/resource_importer.cpp20
-rw-r--r--core/io/resource_loader.cpp4
-rw-r--r--core/object/callable_method_pointer.cpp22
-rw-r--r--core/object/class_db.cpp4
-rw-r--r--core/object/class_db.h4
-rw-r--r--core/object/message_queue.cpp94
-rw-r--r--core/object/message_queue.h2
-rw-r--r--core/object/object.cpp24
-rw-r--r--core/object/object.h10
-rw-r--r--core/object/script_language.h1
-rw-r--r--core/os/main_loop.cpp8
-rw-r--r--core/os/main_loop.h4
-rw-r--r--core/os/os.cpp4
-rw-r--r--core/os/os.h3
-rw-r--r--core/string/locales.h6
-rw-r--r--core/string/print_string.cpp2
-rw-r--r--core/string/translation.cpp9
-rw-r--r--core/string/translation.h2
-rw-r--r--core/templates/hash_map.h34
-rw-r--r--core/variant/array.cpp12
-rw-r--r--doc/classes/@GlobalScope.xml4
-rw-r--r--doc/classes/AStarGrid2D.xml2
-rw-r--r--doc/classes/Area3D.xml2
-rw-r--r--doc/classes/Array.xml1
-rw-r--r--doc/classes/AudioEffectDelay.xml18
-rw-r--r--doc/classes/AudioStreamPlayer3D.xml2
-rw-r--r--doc/classes/BackBufferCopy.xml2
-rw-r--r--doc/classes/BaseMaterial3D.xml2
-rw-r--r--doc/classes/CPUParticles2D.xml9
-rw-r--r--doc/classes/CPUParticles3D.xml9
-rw-r--r--doc/classes/CodeEdit.xml12
-rw-r--r--doc/classes/CollisionPolygon2D.xml2
-rw-r--r--doc/classes/CollisionShape3D.xml4
-rw-r--r--doc/classes/Control.xml22
-rw-r--r--doc/classes/ConvexPolygonShape2D.xml2
-rw-r--r--doc/classes/CylinderShape3D.xml4
-rw-r--r--doc/classes/Dictionary.xml2
-rw-r--r--doc/classes/DisplayServer.xml57
-rw-r--r--doc/classes/EditorDebuggerPlugin.xml2
-rw-r--r--doc/classes/EditorExportPlugin.xml78
-rw-r--r--doc/classes/EditorFeatureProfile.xml2
-rw-r--r--doc/classes/EditorInterface.xml27
-rw-r--r--doc/classes/EditorPlugin.xml35
-rw-r--r--doc/classes/EditorSettings.xml15
-rw-r--r--doc/classes/EditorVCSInterface.xml2
-rw-r--r--doc/classes/FileDialog.xml4
-rw-r--r--doc/classes/FontFile.xml2
-rw-r--r--doc/classes/GPUParticles2D.xml11
-rw-r--r--doc/classes/GPUParticles3D.xml10
-rw-r--r--doc/classes/GraphEdit.xml36
-rw-r--r--doc/classes/GraphNode.xml9
-rw-r--r--doc/classes/HTTPClient.xml1
-rw-r--r--doc/classes/Image.xml30
-rw-r--r--doc/classes/Input.xml9
-rw-r--r--doc/classes/Mesh.xml2
-rw-r--r--doc/classes/NavigationAgent2D.xml16
-rw-r--r--doc/classes/NavigationAgent3D.xml20
-rw-r--r--doc/classes/NavigationMesh.xml10
-rw-r--r--doc/classes/NavigationMeshSourceGeometryData3D.xml2
-rw-r--r--doc/classes/NavigationPolygon.xml6
-rw-r--r--doc/classes/NavigationRegion2D.xml13
-rw-r--r--doc/classes/NavigationRegion3D.xml13
-rw-r--r--doc/classes/NavigationServer2D.xml38
-rw-r--r--doc/classes/NavigationServer3D.xml43
-rw-r--r--doc/classes/Node.xml12
-rw-r--r--doc/classes/Node3D.xml2
-rw-r--r--doc/classes/OS.xml17
-rw-r--r--doc/classes/OpenXRAPIExtension.xml114
-rw-r--r--doc/classes/OpenXRExtensionWrapperExtension.xml167
-rw-r--r--doc/classes/PackedByteArray.xml2
-rw-r--r--doc/classes/PhysicsServer3D.xml4
-rw-r--r--doc/classes/PinJoint3D.xml2
-rw-r--r--doc/classes/ProjectSettings.xml30
-rw-r--r--doc/classes/RayCast3D.xml3
-rw-r--r--doc/classes/Rect2.xml113
-rw-r--r--doc/classes/Rect2i.xml108
-rw-r--r--doc/classes/RenderSceneBuffers.xml21
-rw-r--r--doc/classes/RenderSceneBuffersConfiguration.xml40
-rw-r--r--doc/classes/RenderSceneBuffersExtension.xml41
-rw-r--r--doc/classes/RenderSceneBuffersRD.xml167
-rw-r--r--doc/classes/RenderingDevice.xml52
-rw-r--r--doc/classes/RenderingServer.xml24
-rw-r--r--doc/classes/Resource.xml2
-rw-r--r--doc/classes/ResourceImporterBMFont.xml22
-rw-r--r--doc/classes/ResourceImporterBitMap.xml22
-rw-r--r--doc/classes/ResourceImporterCSVTranslation.xml28
-rw-r--r--doc/classes/ResourceImporterDynamicFont.xml77
-rw-r--r--doc/classes/ResourceImporterImage.xml12
-rw-r--r--doc/classes/ResourceImporterImageFont.xml38
-rw-r--r--doc/classes/ResourceImporterLayeredTexture.xml57
-rw-r--r--doc/classes/ResourceImporterOBJ.xml28
-rw-r--r--doc/classes/ResourceImporterScene.xml70
-rw-r--r--doc/classes/ResourceImporterShaderFile.xml11
-rw-r--r--doc/classes/ResourceImporterTexture.xml106
-rw-r--r--doc/classes/ResourceImporterTextureAtlas.xml29
-rw-r--r--doc/classes/ResourceImporterWAV.xml53
-rw-r--r--doc/classes/ResourceLoader.xml6
-rw-r--r--doc/classes/RichTextLabel.xml23
-rw-r--r--doc/classes/RigidBody2D.xml2
-rw-r--r--doc/classes/RigidBody3D.xml2
-rw-r--r--doc/classes/SceneTree.xml4
-rw-r--r--doc/classes/SeparationRayShape3D.xml2
-rw-r--r--doc/classes/ShaderMaterial.xml1
-rw-r--r--doc/classes/ShapeCast3D.xml4
-rw-r--r--doc/classes/SliderJoint3D.xml2
-rw-r--r--doc/classes/SubViewportContainer.xml1
-rw-r--r--doc/classes/TextEdit.xml2
-rw-r--r--doc/classes/Texture2DArrayRD.xml11
-rw-r--r--doc/classes/Texture2DRD.xml17
-rw-r--r--doc/classes/Texture3DRD.xml16
-rw-r--r--doc/classes/TextureCubemapArrayRD.xml11
-rw-r--r--doc/classes/TextureCubemapRD.xml11
-rw-r--r--doc/classes/TextureLayeredRD.xml16
-rw-r--r--doc/classes/Thread.xml1
-rw-r--r--doc/classes/TileMap.xml56
-rw-r--r--doc/classes/TileSetAtlasSource.xml26
-rw-r--r--doc/classes/Tree.xml12
-rw-r--r--doc/classes/TreeItem.xml9
-rw-r--r--doc/classes/Tween.xml4
-rw-r--r--doc/classes/VideoStreamPlayer.xml10
-rw-r--r--doc/classes/Viewport.xml4
-rw-r--r--doc/classes/VisualShaderNode.xml7
-rw-r--r--doc/classes/VisualShaderNodeCustom.xml8
-rw-r--r--doc/classes/VisualShaderNodeRotationByAxis.xml11
-rw-r--r--doc/classes/VisualShaderNodeScreenNormalWorldSpace.xml11
-rw-r--r--doc/classes/VisualShaderNodeWorldPositionFromDepth.xml11
-rw-r--r--doc/classes/Window.xml7
-rwxr-xr-xdoc/tools/doc_status.py6
-rwxr-xr-xdoc/tools/make_rst.py48
-rw-r--r--drivers/gles3/effects/copy_effects.cpp76
-rw-r--r--drivers/gles3/effects/copy_effects.h1
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp189
-rw-r--r--drivers/gles3/shader_gles3.cpp6
-rw-r--r--drivers/gles3/shaders/copy.glsl39
-rw-r--r--drivers/gles3/shaders/scene.glsl18
-rw-r--r--drivers/gles3/shaders/sky.glsl12
-rw-r--r--drivers/gles3/storage/material_storage.cpp2
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp9
-rw-r--r--drivers/gles3/storage/render_scene_buffers_gles3.cpp32
-rw-r--r--drivers/gles3/storage/render_scene_buffers_gles3.h4
-rw-r--r--drivers/gles3/storage/texture_storage.cpp17
-rw-r--r--drivers/gles3/storage/texture_storage.h34
-rw-r--r--drivers/png/resource_saver_png.cpp2
-rw-r--r--drivers/unix/file_access_unix.cpp5
-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.cpp143
-rw-r--r--drivers/vulkan/rendering_device_vulkan.h4
-rw-r--r--drivers/vulkan/vulkan_context.cpp27
-rw-r--r--editor/animation_track_editor.cpp12
-rw-r--r--editor/animation_track_editor_plugins.cpp4
-rw-r--r--editor/code_editor.cpp19
-rw-r--r--editor/code_editor.h1
-rw-r--r--editor/create_dialog.cpp6
-rw-r--r--editor/debugger/editor_debugger_inspector.cpp2
-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/debugger/script_editor_debugger.cpp207
-rw-r--r--editor/debugger/script_editor_debugger.h37
-rw-r--r--editor/doc_tools.cpp48
-rw-r--r--editor/editor_data.cpp9
-rw-r--r--editor/editor_feature_profile.cpp54
-rw-r--r--editor/editor_feature_profile.h2
-rw-r--r--editor/editor_file_system.cpp2
-rw-r--r--editor/editor_help.cpp153
-rw-r--r--editor/editor_inspector.cpp1
-rw-r--r--editor/editor_inspector.h1
-rw-r--r--editor/editor_interface.cpp17
-rw-r--r--editor/editor_interface.h4
-rw-r--r--editor/editor_log.cpp8
-rw-r--r--editor/editor_node.cpp172
-rw-r--r--editor/editor_node.h3
-rw-r--r--editor/editor_plugin.cpp18
-rw-r--r--editor/editor_plugin.h5
-rw-r--r--editor/editor_properties.cpp8
-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.cpp18
-rw-r--r--editor/editor_settings.h1
-rw-r--r--editor/editor_themes.cpp38
-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_file_dialog.cpp2
-rw-r--r--editor/gui/editor_toaster.cpp1
-rw-r--r--editor/gui/editor_toaster.h1
-rw-r--r--editor/gui/scene_tree_editor.cpp5
-rw-r--r--editor/icons/AnimatedSprite2D.svg2
-rw-r--r--editor/icons/AnimatedSprite3D.svg2
-rw-r--r--editor/icons/AssetLib.svg2
-rw-r--r--editor/icons/AutoKey.svg2
-rw-r--r--editor/icons/Bone.svg2
-rw-r--r--editor/icons/Bone2D.svg2
-rw-r--r--editor/icons/BoneAttachment3D.svg2
-rw-r--r--editor/icons/ClassList.svg2
-rw-r--r--editor/icons/CopyNodePath.svg2
-rw-r--r--editor/icons/CryptoKey.svg2
-rw-r--r--editor/icons/EditorPositionUnselected.svg2
-rw-r--r--editor/icons/Enum.svg2
-rw-r--r--editor/icons/ExpandBottomDock.svg2
-rw-r--r--editor/icons/ExternalLink.svg2
-rw-r--r--editor/icons/Gizmo3DSamplePlayer.svg2
-rw-r--r--editor/icons/GizmoAudioListener3D.svg2
-rw-r--r--editor/icons/GizmoCPUParticles3D.svg2
-rw-r--r--editor/icons/GizmoDirectionalLight.svg2
-rw-r--r--editor/icons/GizmoSpotLight.svg2
-rw-r--r--editor/icons/GizmoVoxelGI.svg2
-rw-r--r--editor/icons/GridToggle.svg1
-rw-r--r--editor/icons/GuiGraphNodePort.svg2
-rw-r--r--editor/icons/GuiIndeterminate.svg2
-rw-r--r--editor/icons/GuiSpinboxUpdown.svg2
-rw-r--r--editor/icons/GuiSpinboxUpdownDisabled.svg2
-rw-r--r--editor/icons/GuiTabMenu.svg2
-rw-r--r--editor/icons/GuiTabMenuHl.svg2
-rw-r--r--editor/icons/GuiVisibilityHidden.svg2
-rw-r--r--editor/icons/HSlider.svg2
-rw-r--r--editor/icons/Heart.svg2
-rw-r--r--editor/icons/Help.svg2
-rw-r--r--editor/icons/IOSDeviceWired.svg1
-rw-r--r--editor/icons/IOSDeviceWireless.svg1
-rw-r--r--editor/icons/IOSSimulator.svg1
-rw-r--r--editor/icons/Info.svg1
-rw-r--r--editor/icons/KeyInvalid.svg2
-rw-r--r--editor/icons/KeyPosition.svg2
-rw-r--r--editor/icons/KeyScale.svg2
-rw-r--r--editor/icons/Load.svg2
-rw-r--r--editor/icons/LockViewport.svg2
-rw-r--r--editor/icons/Loop.svg2
-rw-r--r--editor/icons/MatchCase.svg2
-rw-r--r--editor/icons/MeshLibrary.svg2
-rw-r--r--editor/icons/MeshTexture.svg2
-rw-r--r--editor/icons/MethodOverrideAndSlot.svg2
-rw-r--r--editor/icons/NodeInfo.svg2
-rw-r--r--editor/icons/ORMMaterial3D.svg2
-rw-r--r--editor/icons/Play.svg2
-rw-r--r--editor/icons/Popup.svg2
-rw-r--r--editor/icons/ProgressBar.svg2
-rw-r--r--editor/icons/ProxyTexture.svg2
-rw-r--r--editor/icons/Rename.svg2
-rw-r--r--editor/icons/ShaderGlobalsOverride.svg2
-rw-r--r--editor/icons/Sort.svg2
-rw-r--r--editor/icons/SpotLight3D.svg2
-rw-r--r--editor/icons/SpriteFrames.svg2
-rw-r--r--editor/icons/SpriteSheet.svg2
-rw-r--r--editor/icons/TextureButton.svg2
-rw-r--r--editor/icons/ToolPan.svg2
-rw-r--r--editor/icons/Tools.svg2
-rw-r--r--editor/icons/TouchScreenButton.svg2
-rw-r--r--editor/icons/TrackCapture.svg2
-rw-r--r--editor/icons/TrackContinuous.svg2
-rw-r--r--editor/icons/TransitionImmediate.svg2
-rw-r--r--editor/icons/TransitionImmediateAuto.svg2
-rw-r--r--editor/icons/Transpose.svg1
-rw-r--r--editor/icons/Tree.svg2
-rw-r--r--editor/icons/VSlider.svg2
-rw-r--r--editor/icons/VehicleBody3D.svg2
-rw-r--r--editor/icons/ViewportTexture.svg2
-rw-r--r--editor/icons/VisualShaderNodeBooleanUniform.svg2
-rw-r--r--editor/icons/VisualShaderNodeCubemap.svg2
-rw-r--r--editor/icons/VisualShaderNodeExpression.svg2
-rw-r--r--editor/icons/VisualShaderNodeGlobalExpression.svg2
-rw-r--r--editor/icons/VisualShaderNodeTransformUniform.svg2
-rw-r--r--editor/icons/VoxelGIData.svg2
-rw-r--r--editor/import/audio_stream_import_settings.cpp4
-rw-r--r--editor/import/resource_importer_imagefont.cpp2
-rw-r--r--editor/import/resource_importer_layered_texture.cpp1
-rw-r--r--editor/import/resource_importer_scene.cpp22
-rw-r--r--editor/import/resource_importer_texture.cpp10
-rw-r--r--editor/import/resource_importer_texture_atlas.cpp4
-rw-r--r--editor/import/scene_import_settings.cpp219
-rw-r--r--editor/import/scene_import_settings.h20
-rw-r--r--editor/import_defaults_editor.cpp8
-rw-r--r--editor/import_dock.cpp95
-rw-r--r--editor/import_dock.h6
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp13
-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.cpp5
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp19
-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/cpu_particles_2d_editor_plugin.cpp16
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.h1
-rw-r--r--editor/plugins/curve_editor_plugin.cpp10
-rw-r--r--editor/plugins/debugger_editor_plugin.cpp3
-rw-r--r--editor/plugins/dedicated_server_export_plugin.h2
-rw-r--r--editor/plugins/editor_preview_plugins.cpp3
-rw-r--r--editor/plugins/font_config_plugin.cpp1
-rw-r--r--editor/plugins/gdextension_export_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp19
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.h1
-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.cpp38
-rw-r--r--editor/plugins/node_3d_editor_plugin.h3
-rw-r--r--editor/plugins/polygon_3d_editor_plugin.cpp5
-rw-r--r--editor/plugins/script_editor_plugin.cpp78
-rw-r--r--editor/plugins/script_editor_plugin.h2
-rw-r--r--editor/plugins/script_text_editor.cpp97
-rw-r--r--editor/plugins/script_text_editor.h1
-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.cpp17
-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/theme_editor_preview.cpp2
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.cpp1
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp31
-rw-r--r--editor/plugins/tiles/tile_data_editors.h5
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp69
-rw-r--r--editor/plugins/tiles/tile_map_editor.h16
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp229
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h13
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp215
-rw-r--r--editor/plugins/tiles/tile_set_editor.h40
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp6
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp327
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h82
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp124
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.cpp9
-rw-r--r--editor/pot_generator.cpp56
-rw-r--r--editor/project_converter_3_to_4.cpp55
-rw-r--r--editor/project_manager.cpp114
-rw-r--r--editor/project_manager.h4
-rw-r--r--editor/register_editor_types.cpp30
-rw-r--r--editor/script_create_dialog.cpp51
-rw-r--r--editor/shader_create_dialog.cpp69
-rw-r--r--editor/window_wrapper.cpp2
-rw-r--r--main/main.cpp19
-rw-r--r--misc/extension_api_validation/4.0-stable.expected21
-rw-r--r--modules/csg/csg_shape.cpp4
-rw-r--r--modules/dds/image_loader_dds.cpp422
-rw-r--r--modules/dds/image_loader_dds.h (renamed from modules/mono/mono_gd/support/android_support.h)33
-rw-r--r--modules/dds/register_types.cpp8
-rw-r--r--modules/dds/texture_loader_dds.cpp363
-rw-r--r--modules/dds/texture_loader_dds.h1
-rw-r--r--modules/enet/enet_packet_peer.cpp2
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml3
-rw-r--r--modules/gdscript/editor/gdscript_docgen.cpp44
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.cpp107
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h10
-rw-r--r--modules/gdscript/gdscript.cpp43
-rw-r--r--modules/gdscript/gdscript.h68
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp70
-rw-r--r--modules/gdscript/gdscript_analyzer.h3
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp14
-rw-r--r--modules/gdscript/gdscript_cache.cpp5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp349
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp3
-rw-r--r--modules/gdscript/gdscript_editor.cpp203
-rw-r--r--modules/gdscript/gdscript_function.h14
-rw-r--r--modules/gdscript/gdscript_parser.cpp190
-rw-r--r--modules/gdscript/gdscript_parser.h57
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp32
-rw-r--r--modules/gdscript/gdscript_vm.cpp94
-rw-r--r--modules/gdscript/gdscript_warning.cpp8
-rw-r--r--modules/gdscript/gdscript_warning.h4
-rw-r--r--modules/gdscript/register_types.cpp2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/allow_get_node_with_onready.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/allow_get_node_with_onready.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.out7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out16
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.gd17
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out5
-rw-r--r--modules/gltf/gltf_document.cpp66
-rw-r--r--modules/gltf/register_types.cpp4
-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/minimp3/config.py1
-rw-r--r--modules/minimp3/doc_classes/ResourceImporterMP3.xml37
-rw-r--r--modules/minimp3/register_types.cpp4
-rw-r--r--modules/mono/SCsub2
-rw-r--r--modules/mono/config.py2
-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/ScriptMethodsGenerator.cs22
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs34
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs15
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs23
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs64
-rw-r--r--modules/mono/editor/bindings_generator.cpp78
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs30
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs584
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs40
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs12
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs104
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs1
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs25
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs40
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs2
-rw-r--r--modules/mono/godotsharp_dirs.cpp55
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp8
-rw-r--r--modules/mono/mono_gd/support/android_support.cpp720
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.cpp4
-rw-r--r--modules/navigation/godot_navigation_server.cpp36
-rw-r--r--modules/navigation/godot_navigation_server.h7
-rw-r--r--modules/navigation/nav_link.cpp10
-rw-r--r--modules/navigation/nav_link.h4
-rw-r--r--modules/navigation/nav_map.cpp6
-rw-r--r--modules/navigation/nav_region.cpp14
-rw-r--r--modules/navigation/nav_region.h4
-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/SCsub114
-rw-r--r--modules/openxr/config.py2
-rw-r--r--modules/openxr/extensions/openxr_android_extension.cpp1
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper.h2
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.cpp207
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.h115
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.cpp4
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.cpp4
-rw-r--r--modules/openxr/extensions/openxr_vulkan_extension.cpp4
-rw-r--r--modules/openxr/openxr_api.cpp7
-rw-r--r--modules/openxr/openxr_api.h2
-rw-r--r--modules/openxr/openxr_api_extension.cpp130
-rw-r--r--modules/openxr/openxr_api_extension.h76
-rw-r--r--modules/openxr/openxr_util.cpp88
-rw-r--r--modules/openxr/openxr_util.h21
-rw-r--r--modules/openxr/register_types.cpp8
-rw-r--r--modules/openxr/util.h11
-rw-r--r--modules/svg/image_loader_svg.cpp22
-rw-r--r--modules/svg/image_loader_svg.h12
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct1
-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/gdextension_build/SConstruct1
-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/config.py1
-rw-r--r--modules/vorbis/doc_classes/AudioStreamOggVorbis.xml20
-rw-r--r--modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml53
-rw-r--r--modules/vorbis/register_types.cpp7
-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--modules/webp/webp_common.cpp1
-rw-r--r--platform/android/android_input_handler.cpp5
-rw-r--r--platform/android/android_input_handler.h2
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml12
-rw-r--r--platform/android/export/export_plugin.cpp217
-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.kt973
-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/GodotLib.java2
-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/input/GodotInputHandler.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java16
-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.cpp6
-rw-r--r--platform/android/java_godot_lib_jni.h2
-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.cpp5
-rw-r--r--platform/ios/export/export_plugin.cpp486
-rw-r--r--platform/ios/export/export_plugin.h98
-rw-r--r--platform/ios/export/run_icon.svg1
-rw-r--r--platform/linuxbsd/detect.py3
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp5
-rw-r--r--platform/linuxbsd/export/export_plugin.h2
-rw-r--r--platform/linuxbsd/export/run_icon.svg2
-rw-r--r--platform/linuxbsd/joypad_linux.cpp34
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp34
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp40
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h1
-rw-r--r--platform/macos/display_server_macos.h6
-rw-r--r--platform/macos/display_server_macos.mm286
-rw-r--r--platform/macos/doc_classes/EditorExportPlatformMacOS.xml3
-rw-r--r--platform/macos/export/export_plugin.cpp17
-rw-r--r--platform/macos/export/run_icon.svg2
-rw-r--r--platform/macos/os_macos.h4
-rw-r--r--platform/macos/os_macos.mm59
-rw-r--r--platform/uwp/export/export_plugin.cpp20
-rw-r--r--platform/uwp/export/export_plugin.h1
-rw-r--r--platform/web/display_server_web.cpp52
-rw-r--r--platform/web/export/export_plugin.cpp22
-rw-r--r--platform/web/export/run_icon.svg2
-rw-r--r--platform/web/http_client_web.cpp10
-rw-r--r--platform/web/http_client_web.h1
-rw-r--r--platform/web/js/libs/library_godot_display.js25
-rw-r--r--platform/web/js/libs/library_godot_fetch.js15
-rw-r--r--platform/windows/display_server_windows.cpp328
-rw-r--r--platform/windows/display_server_windows.h6
-rw-r--r--platform/windows/export/export_plugin.cpp5
-rw-r--r--platform/windows/export/run_icon.svg2
-rw-r--r--platform/windows/gl_manager_windows.cpp1
-rw-r--r--scene/2d/camera_2d.cpp2
-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.cpp64
-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_link_2d.cpp10
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp24
-rw-r--r--scene/2d/navigation_region_2d.cpp143
-rw-r--r--scene/2d/navigation_region_2d.h7
-rw-r--r--scene/2d/path_2d.cpp4
-rw-r--r--scene/2d/physics_body_2d.cpp26
-rw-r--r--scene/2d/physics_body_2d.h2
-rw-r--r--scene/2d/shape_cast_2d.cpp5
-rw-r--r--scene/2d/skeleton_2d.cpp4
-rw-r--r--scene/2d/sprite_2d.cpp5
-rw-r--r--scene/2d/tile_map.compat.inc (renamed from modules/mono/mono_gd/android_mono_config.h)20
-rw-r--r--scene/2d/tile_map.cpp4160
-rw-r--r--scene/2d/tile_map.h407
-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.cpp62
-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_link_3d.cpp10
-rw-r--r--scene/3d/navigation_region_3d.cpp139
-rw-r--r--scene/3d/navigation_region_3d.h9
-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.cpp45
-rw-r--r--scene/3d/physics_body_3d.h3
-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.cpp22
-rw-r--r--scene/animation/animation_blend_tree.h2
-rw-r--r--scene/animation/tween.cpp8
-rw-r--r--scene/debugger/scene_debugger.cpp8
-rw-r--r--scene/gui/button.cpp14
-rw-r--r--scene/gui/code_edit.cpp26
-rw-r--r--scene/gui/code_edit.h3
-rw-r--r--scene/gui/color_mode.cpp85
-rw-r--r--scene/gui/color_mode.h10
-rw-r--r--scene/gui/color_picker.cpp15
-rw-r--r--scene/gui/color_picker.h6
-rw-r--r--scene/gui/control.cpp34
-rw-r--r--scene/gui/dialogs.cpp26
-rw-r--r--scene/gui/dialogs.h1
-rw-r--r--scene/gui/file_dialog.cpp43
-rw-r--r--scene/gui/file_dialog.h8
-rw-r--r--scene/gui/graph_edit.cpp1486
-rw-r--r--scene/gui/graph_edit.h178
-rw-r--r--scene/gui/graph_edit_arranger.cpp565
-rw-r--r--scene/gui/graph_edit_arranger.h67
-rw-r--r--scene/gui/graph_node.cpp46
-rw-r--r--scene/gui/graph_node.h5
-rw-r--r--scene/gui/item_list.cpp3
-rw-r--r--scene/gui/label.cpp11
-rw-r--r--scene/gui/menu_bar.cpp2
-rw-r--r--scene/gui/nine_patch_rect.cpp5
-rw-r--r--scene/gui/option_button.cpp12
-rw-r--r--scene/gui/popup_menu.cpp15
-rw-r--r--scene/gui/popup_menu.h2
-rw-r--r--scene/gui/rich_text_label.cpp116
-rw-r--r--scene/gui/rich_text_label.h22
-rw-r--r--scene/gui/slider.cpp2
-rw-r--r--scene/gui/subviewport_container.cpp27
-rw-r--r--scene/gui/subviewport_container.h2
-rw-r--r--scene/gui/tab_bar.cpp3
-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/tree.cpp37
-rw-r--r--scene/gui/tree.h5
-rw-r--r--scene/gui/video_stream_player.cpp25
-rw-r--r--scene/gui/video_stream_player.h7
-rw-r--r--scene/main/canvas_item.h4
-rw-r--r--scene/main/node.cpp17
-rw-r--r--scene/main/scene_tree.cpp76
-rw-r--r--scene/main/scene_tree.h5
-rw-r--r--scene/main/viewport.cpp286
-rw-r--r--scene/main/viewport.h18
-rw-r--r--scene/main/window.cpp62
-rw-r--r--scene/main/window.h3
-rw-r--r--scene/register_scene_types.cpp25
-rw-r--r--scene/resources/animated_texture.cpp286
-rw-r--r--scene/resources/animated_texture.h109
-rw-r--r--scene/resources/animation.cpp15
-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.cpp200
-rw-r--r--scene/resources/default_theme/grid_toggle.svg1
-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.cpp9
-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/navigation_mesh.cpp7
-rw-r--r--scene/resources/navigation_mesh.h17
-rw-r--r--scene/resources/navigation_polygon.cpp11
-rw-r--r--scene/resources/navigation_polygon.h2
-rw-r--r--scene/resources/packed_scene.h1
-rw-r--r--scene/resources/particle_process_material.cpp1
-rw-r--r--scene/resources/placeholder_textures.cpp197
-rw-r--r--scene/resources/placeholder_textures.h132
-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.cpp22
-rw-r--r--scene/resources/resource_format_text.h2
-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/texture_rd.cpp346
-rw-r--r--scene/resources/texture_rd.h153
-rw-r--r--scene/resources/theme.cpp34
-rw-r--r--scene/resources/tile_set.cpp45
-rw-r--r--scene/resources/tile_set.h14
-rw-r--r--scene/resources/video_stream.cpp2
-rw-r--r--scene/resources/visual_shader.cpp23
-rw-r--r--scene/resources/visual_shader.h3
-rw-r--r--scene/resources/visual_shader_nodes.cpp236
-rw-r--r--scene/resources/visual_shader_nodes.h70
-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_3d.cpp1
-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.cpp28
-rw-r--r--servers/display_server.h13
-rw-r--r--servers/navigation_server_2d.cpp8
-rw-r--r--servers/navigation_server_2d.h6
-rw-r--r--servers/navigation_server_3d.cpp13
-rw-r--r--servers/navigation_server_3d.h8
-rw-r--r--servers/navigation_server_3d_dummy.h6
-rw-r--r--servers/register_server_types.cpp7
-rw-r--r--servers/rendering/dummy/storage/texture_storage.h3
-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/effects/debug_effects.cpp328
-rw-r--r--servers/rendering/renderer_rd/effects/debug_effects.h85
-rw-r--r--servers/rendering/renderer_rd/effects/ss_effects.cpp3
-rw-r--r--servers/rendering/renderer_rd/environment/fog.cpp28
-rw-r--r--servers/rendering/renderer_rd/environment/fog.h2
-rw-r--r--servers/rendering/renderer_rd/environment/sky.cpp242
-rw-r--r--servers/rendering/renderer_rd/environment/sky.h3
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp158
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h14
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp99
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h14
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp92
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h11
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp3
-rw-r--r--servers/rendering/renderer_rd/pipeline_cache_rd.cpp2
-rw-r--r--servers/rendering/renderer_rd/pipeline_cache_rd.h6
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp28
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp51
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h4
-rw-r--r--servers/rendering/renderer_rd/shader_rd.cpp274
-rw-r--r--servers/rendering/renderer_rd/shader_rd.h53
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl6
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl15
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl4
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/shadow_frustum.glsl41
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/sky.glsl29
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl15
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl76
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl15
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl42
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl15
-rw-r--r--servers/rendering/renderer_rd/shaders/particles.glsl37
-rw-r--r--servers/rendering/renderer_rd/shaders/samplers_inc.glsl12
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl12
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl14
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp20
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.h2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.cpp37
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.h8
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp125
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h91
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.cpp436
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.h24
-rw-r--r--servers/rendering/renderer_scene_cull.cpp5
-rw-r--r--servers/rendering/renderer_viewport.cpp14
-rw-r--r--servers/rendering/rendering_device.cpp17
-rw-r--r--servers/rendering/rendering_device.h14
-rw-r--r--servers/rendering/rendering_device_binds.h3
-rw-r--r--servers/rendering/rendering_server_default.cpp18
-rw-r--r--servers/rendering/rendering_server_default.h44
-rw-r--r--servers/rendering/shader_compiler.cpp18
-rw-r--r--servers/rendering/shader_compiler.h1
-rw-r--r--servers/rendering/shader_language.cpp43
-rw-r--r--servers/rendering/storage/render_scene_buffers.cpp57
-rw-r--r--servers/rendering/storage/render_scene_buffers.h92
-rw-r--r--servers/rendering/storage/texture_storage.h3
-rw-r--r--servers/rendering_server.cpp5
-rw-r--r--servers/rendering_server.h5
-rw-r--r--servers/text_server.cpp4
-rw-r--r--tests/core/input/test_input_event.h115
-rw-r--r--tests/core/io/test_image.h90
-rw-r--r--tests/core/io/test_json.h84
-rw-r--r--tests/core/io/test_resource.h52
-rw-r--r--tests/core/object/test_object.h23
-rw-r--r--tests/core/variant/test_array.h26
-rw-r--r--tests/data/images/icon.ddsbin0 -> 87536 bytes
-rw-r--r--tests/scene/test_theme.h2
-rw-r--r--tests/scene/test_viewport.h41
-rw-r--r--tests/servers/test_navigation_server_3d.h481
-rw-r--r--tests/test_main.cpp1
-rw-r--r--thirdparty/README.md24
-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
-rw-r--r--thirdparty/freetype/include/freetype/config/ftoption.h47
-rw-r--r--thirdparty/freetype/include/freetype/config/ftstdlib.h14
-rw-r--r--thirdparty/freetype/include/freetype/freetype.h563
-rw-r--r--thirdparty/freetype/include/freetype/ftcache.h88
-rw-r--r--thirdparty/freetype/include/freetype/ftchapters.h23
-rw-r--r--thirdparty/freetype/include/freetype/ftdriver.h11
-rw-r--r--thirdparty/freetype/include/freetype/ftimage.h10
-rw-r--r--thirdparty/freetype/include/freetype/ftlogging.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftmm.h57
-rw-r--r--thirdparty/freetype/include/freetype/ftoutln.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftrender.h2
-rw-r--r--thirdparty/freetype/include/freetype/ftsynth.h12
-rw-r--r--thirdparty/freetype/include/freetype/ftsystem.h16
-rw-r--r--thirdparty/freetype/include/freetype/internal/compiler-macros.h7
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftdrv.h1
-rw-r--r--thirdparty/freetype/include/freetype/internal/ftmmtypes.h20
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svmetric.h10
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svmm.h109
-rw-r--r--thirdparty/freetype/include/freetype/internal/services/svpscmap.h2
-rw-r--r--thirdparty/freetype/include/freetype/internal/t1types.h26
-rw-r--r--thirdparty/freetype/include/freetype/internal/tttypes.h122
-rw-r--r--thirdparty/freetype/src/autofit/afcjk.c56
-rw-r--r--thirdparty/freetype/src/autofit/afcjk.h20
-rw-r--r--thirdparty/freetype/src/autofit/afglobal.c5
-rw-r--r--thirdparty/freetype/src/autofit/afglobal.h2
-rw-r--r--thirdparty/freetype/src/autofit/afhints.c58
-rw-r--r--thirdparty/freetype/src/autofit/afindic.c32
-rw-r--r--thirdparty/freetype/src/autofit/aflatin.c49
-rw-r--r--thirdparty/freetype/src/autofit/aflatin.h4
-rw-r--r--thirdparty/freetype/src/autofit/afloader.c6
-rw-r--r--thirdparty/freetype/src/autofit/afmodule.c37
-rw-r--r--thirdparty/freetype/src/autofit/afshaper.c6
-rw-r--r--thirdparty/freetype/src/autofit/ft-hb.c2
-rw-r--r--thirdparty/freetype/src/base/ftbbox.c42
-rw-r--r--thirdparty/freetype/src/base/ftcalc.c4
-rw-r--r--thirdparty/freetype/src/base/ftdbgmem.c2
-rw-r--r--thirdparty/freetype/src/base/ftmac.c2
-rw-r--r--thirdparty/freetype/src/base/ftmm.c146
-rw-r--r--thirdparty/freetype/src/base/ftobjs.c23
-rw-r--r--thirdparty/freetype/src/base/ftoutln.c52
-rw-r--r--thirdparty/freetype/src/base/ftstroke.c21
-rw-r--r--thirdparty/freetype/src/base/ftsynth.c20
-rw-r--r--thirdparty/freetype/src/base/ftsystem.c9
-rw-r--r--thirdparty/freetype/src/bdf/bdf.h4
-rw-r--r--thirdparty/freetype/src/bdf/bdfdrivr.c116
-rw-r--r--thirdparty/freetype/src/bdf/bdflib.c67
-rw-r--r--thirdparty/freetype/src/bzip2/ftbzip2.c19
-rw-r--r--thirdparty/freetype/src/cache/ftcbasic.c8
-rw-r--r--thirdparty/freetype/src/cache/ftccache.c125
-rw-r--r--thirdparty/freetype/src/cache/ftccache.h17
-rw-r--r--thirdparty/freetype/src/cache/ftcglyph.c46
-rw-r--r--thirdparty/freetype/src/cache/ftcglyph.h15
-rw-r--r--thirdparty/freetype/src/cache/ftcmanag.c42
-rw-r--r--thirdparty/freetype/src/cache/ftcmru.c28
-rw-r--r--thirdparty/freetype/src/cache/ftcsbits.c17
-rw-r--r--thirdparty/freetype/src/cache/ftcsbits.h11
-rw-r--r--thirdparty/freetype/src/cff/cffcmap.c107
-rw-r--r--thirdparty/freetype/src/cff/cffdrivr.c443
-rw-r--r--thirdparty/freetype/src/cff/cffgload.c6
-rw-r--r--thirdparty/freetype/src/cff/cffload.c31
-rw-r--r--thirdparty/freetype/src/cff/cffload.h4
-rw-r--r--thirdparty/freetype/src/cff/cffobjs.c24
-rw-r--r--thirdparty/freetype/src/cff/cffparse.c228
-rw-r--r--thirdparty/freetype/src/cff/cffparse.h9
-rw-r--r--thirdparty/freetype/src/cid/cidgload.c141
-rw-r--r--thirdparty/freetype/src/cid/cidgload.h8
-rw-r--r--thirdparty/freetype/src/cid/cidload.c51
-rw-r--r--thirdparty/freetype/src/cid/cidobjs.c14
-rw-r--r--thirdparty/freetype/src/cid/cidparse.c16
-rw-r--r--thirdparty/freetype/src/cid/cidriver.c97
-rw-r--r--thirdparty/freetype/src/gxvalid/gxvfgen.c3
-rw-r--r--thirdparty/freetype/src/gzip/ftgzip.c5
-rw-r--r--thirdparty/freetype/src/gzip/infback.c644
-rw-r--r--thirdparty/freetype/src/pcf/pcfdrivr.c122
-rw-r--r--thirdparty/freetype/src/pfr/pfrcmap.c48
-rw-r--r--thirdparty/freetype/src/pfr/pfrdrivr.c26
-rw-r--r--thirdparty/freetype/src/pfr/pfrgload.c3
-rw-r--r--thirdparty/freetype/src/pfr/pfrload.c60
-rw-r--r--thirdparty/freetype/src/pfr/pfrobjs.c6
-rw-r--r--thirdparty/freetype/src/psaux/afmparse.c2
-rw-r--r--thirdparty/freetype/src/psaux/t1cmap.c141
-rw-r--r--thirdparty/freetype/src/pshinter/pshalgo.c2
-rw-r--r--thirdparty/freetype/src/pshinter/pshmod.c9
-rw-r--r--thirdparty/freetype/src/pshinter/pshrec.c63
-rw-r--r--thirdparty/freetype/src/psnames/psmodule.c42
-rw-r--r--thirdparty/freetype/src/raster/ftraster.c44
-rw-r--r--thirdparty/freetype/src/raster/ftrend1.c21
-rw-r--r--thirdparty/freetype/src/sdf/ftbsdf.c7
-rw-r--r--thirdparty/freetype/src/sdf/ftsdf.c21
-rw-r--r--thirdparty/freetype/src/sdf/ftsdfrend.c35
-rw-r--r--thirdparty/freetype/src/sfnt/pngshim.c7
-rw-r--r--thirdparty/freetype/src/sfnt/sfdriver.c143
-rw-r--r--thirdparty/freetype/src/sfnt/sfobjs.c30
-rw-r--r--thirdparty/freetype/src/sfnt/sfwoff.c2
-rw-r--r--thirdparty/freetype/src/sfnt/sfwoff2.c2
-rw-r--r--thirdparty/freetype/src/sfnt/ttbdf.c13
-rw-r--r--thirdparty/freetype/src/sfnt/ttbdf.h2
-rw-r--r--thirdparty/freetype/src/sfnt/ttcmap.c547
-rw-r--r--thirdparty/freetype/src/sfnt/ttcolr.c6
-rw-r--r--thirdparty/freetype/src/sfnt/ttcpal.c2
-rw-r--r--thirdparty/freetype/src/sfnt/ttload.c7
-rw-r--r--thirdparty/freetype/src/sfnt/ttmtx.c2
-rw-r--r--thirdparty/freetype/src/sfnt/ttpost.c284
-rw-r--r--thirdparty/freetype/src/sfnt/ttsbit.c2
-rw-r--r--thirdparty/freetype/src/sfnt/ttsvg.c2
-rw-r--r--thirdparty/freetype/src/sfnt/woff2tags.c2
-rw-r--r--thirdparty/freetype/src/smooth/ftgrays.c49
-rw-r--r--thirdparty/freetype/src/smooth/ftsmooth.c22
-rw-r--r--thirdparty/freetype/src/svg/ftsvg.c45
-rw-r--r--thirdparty/freetype/src/truetype/ttdriver.c158
-rw-r--r--thirdparty/freetype/src/truetype/ttgload.c270
-rw-r--r--thirdparty/freetype/src/truetype/ttgxvar.c518
-rw-r--r--thirdparty/freetype/src/truetype/ttgxvar.h40
-rw-r--r--thirdparty/freetype/src/truetype/ttinterp.c145
-rw-r--r--thirdparty/freetype/src/truetype/ttinterp.h10
-rw-r--r--thirdparty/freetype/src/truetype/ttobjs.c85
-rw-r--r--thirdparty/freetype/src/truetype/ttobjs.h6
-rw-r--r--thirdparty/freetype/src/truetype/ttpload.c37
-rw-r--r--thirdparty/freetype/src/truetype/ttpload.h6
-rw-r--r--thirdparty/freetype/src/truetype/ttsubpix.c2
-rw-r--r--thirdparty/freetype/src/type1/t1afm.c4
-rw-r--r--thirdparty/freetype/src/type1/t1driver.c93
-rw-r--r--thirdparty/freetype/src/type1/t1load.c256
-rw-r--r--thirdparty/freetype/src/type1/t1load.h22
-rw-r--r--thirdparty/freetype/src/type1/t1objs.c8
-rw-r--r--thirdparty/freetype/src/type42/t42drivr.c43
-rw-r--r--thirdparty/freetype/src/type42/t42parse.c92
-rw-r--r--thirdparty/freetype/src/winfonts/winfnt.c40
-rw-r--r--thirdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh11
-rw-r--r--thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh4
-rw-r--r--thirdparty/harfbuzz/src/OT/Color/sbix/sbix.hh5
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh3
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh2
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh2
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh3
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh9
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh15
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh1
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh2
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh11
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh5
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh3
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh3
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh64
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GSUB/Common.hh2
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GSUB/Ligature.hh3
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh1
-rw-r--r--thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh2
-rw-r--r--thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh41
-rw-r--r--thirdparty/harfbuzz/src/OT/glyf/Glyph.hh40
-rw-r--r--thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh12
-rw-r--r--thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh4
-rw-r--r--thirdparty/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh2
-rw-r--r--thirdparty/harfbuzz/src/OT/glyf/coord-setter.hh2
-rw-r--r--thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh67
-rw-r--r--thirdparty/harfbuzz/src/OT/glyf/glyf.hh82
-rw-r--r--thirdparty/harfbuzz/src/OT/glyf/path-builder.hh23
-rw-r--r--thirdparty/harfbuzz/src/OT/name/name.hh19
-rw-r--r--thirdparty/harfbuzz/src/graph/classdef-graph.hh8
-rw-r--r--thirdparty/harfbuzz/src/graph/coverage-graph.hh8
-rw-r--r--thirdparty/harfbuzz/src/graph/graph.hh7
-rw-r--r--thirdparty/harfbuzz/src/graph/gsubgpos-context.cc6
-rw-r--r--thirdparty/harfbuzz/src/graph/gsubgpos-context.hh4
-rw-r--r--thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh12
-rw-r--r--thirdparty/harfbuzz/src/graph/pairpos-graph.hh10
-rw-r--r--thirdparty/harfbuzz/src/graph/serialize.hh3
-rw-r--r--thirdparty/harfbuzz/src/hb-algs.hh183
-rw-r--r--thirdparty/harfbuzz/src/hb-array.hh70
-rw-r--r--thirdparty/harfbuzz/src/hb-atomic.hh1
-rw-r--r--thirdparty/harfbuzz/src/hb-bimap.hh74
-rw-r--r--thirdparty/harfbuzz/src/hb-bit-page.hh5
-rw-r--r--thirdparty/harfbuzz/src/hb-bit-set-invertible.hh7
-rw-r--r--thirdparty/harfbuzz/src/hb-bit-set.hh9
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer-verify.cc76
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer.hh7
-rw-r--r--thirdparty/harfbuzz/src/hb-cache.hh8
-rw-r--r--thirdparty/harfbuzz/src/hb-cairo-utils.cc4
-rw-r--r--thirdparty/harfbuzz/src/hb-cairo.cc2
-rw-r--r--thirdparty/harfbuzz/src/hb-cff-interp-common.hh4
-rw-r--r--thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh8
-rw-r--r--thirdparty/harfbuzz/src/hb-common.h10
-rw-r--r--thirdparty/harfbuzz/src/hb-config.hh1
-rw-r--r--thirdparty/harfbuzz/src/hb-debug.hh4
-rw-r--r--thirdparty/harfbuzz/src/hb-deprecated.h46
-rw-r--r--thirdparty/harfbuzz/src/hb-draw.hh30
-rw-r--r--thirdparty/harfbuzz/src/hb-font.cc7
-rw-r--r--thirdparty/harfbuzz/src/hb-font.h45
-rw-r--r--thirdparty/harfbuzz/src/hb-ft.cc2
-rw-r--r--thirdparty/harfbuzz/src/hb-gobject-structs.cc2
-rw-r--r--thirdparty/harfbuzz/src/hb-graphite2.cc42
-rw-r--r--thirdparty/harfbuzz/src/hb-iter.hh9
-rw-r--r--thirdparty/harfbuzz/src/hb-kern.hh2
-rw-r--r--thirdparty/harfbuzz/src/hb-limits.hh4
-rw-r--r--thirdparty/harfbuzz/src/hb-machinery.hh15
-rw-r--r--thirdparty/harfbuzz/src/hb-map.h2
-rw-r--r--thirdparty/harfbuzz/src/hb-map.hh174
-rw-r--r--thirdparty/harfbuzz/src/hb-meta.hh8
-rw-r--r--thirdparty/harfbuzz/src/hb-multimap.hh34
-rw-r--r--thirdparty/harfbuzz/src/hb-null.hh15
-rw-r--r--thirdparty/harfbuzz/src/hb-open-file.hh4
-rw-r--r--thirdparty/harfbuzz/src/hb-open-type.hh63
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff-common.hh271
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff1-table.cc6
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff1-table.hh286
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff2-table.hh33
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cmap-table.hh38
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-font.cc14
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh43
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh77
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh21
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-common.hh137
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh180
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout.cc195
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout.h30
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout.hh14
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-map.cc8
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-map.hh8
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-math-table.hh13
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-os2-table.hh7
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh12
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-post-table.hh5
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape.cc7
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh2
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh14
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.cc12
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shaper-use.cc3
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-stat-table.hh53
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-tag.cc2
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-common.hh942
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-cvar-table.hh69
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh82
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh123
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh59
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-vorg-table.hh2
-rw-r--r--thirdparty/harfbuzz/src/hb-pool.hh2
-rw-r--r--thirdparty/harfbuzz/src/hb-sanitize.hh97
-rw-r--r--thirdparty/harfbuzz/src/hb-serialize.hh34
-rw-r--r--thirdparty/harfbuzz/src/hb-set-digest.hh12
-rw-r--r--thirdparty/harfbuzz/src/hb-set.h2
-rw-r--r--thirdparty/harfbuzz/src/hb-set.hh4
-rw-r--r--thirdparty/harfbuzz/src/hb-shape.cc2
-rw-r--r--thirdparty/harfbuzz/src/hb-shaper-list.hh5
-rw-r--r--thirdparty/harfbuzz/src/hb-static.cc3
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-accelerator.hh51
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff-common.cc29
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff-common.hh110
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff1.cc298
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff2.cc93
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-input.cc49
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-input.hh3
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc175
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-instancer-solver.hh90
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-plan-member-list.hh16
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-plan.cc177
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-plan.hh77
-rw-r--r--thirdparty/harfbuzz/src/hb-subset.cc144
-rw-r--r--thirdparty/harfbuzz/src/hb-subset.h7
-rw-r--r--thirdparty/harfbuzz/src/hb-vector.hh122
-rw-r--r--thirdparty/harfbuzz/src/hb-version.h6
-rw-r--r--thirdparty/harfbuzz/src/hb.hh8
-rw-r--r--thirdparty/icu4c/common/unicode/ures.h9
-rw-r--r--thirdparty/icu4c/common/unicode/uvernum.h6
-rw-r--r--thirdparty/icu4c/icudt73l.datbin4624736 -> 4624768 bytes
-rw-r--r--thirdparty/minizip/patches/empty-zip-fix.patch193
-rw-r--r--thirdparty/minizip/unzip.c92
-rw-r--r--thirdparty/openxr/COPYING.adoc2
-rw-r--r--thirdparty/openxr/include/openxr/openxr.h1399
-rw-r--r--thirdparty/openxr/include/openxr/openxr_platform.h32
-rw-r--r--thirdparty/openxr/include/openxr/openxr_platform_defines.h8
-rw-r--r--thirdparty/openxr/include/openxr/openxr_reflection.h996
-rw-r--r--thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h6
-rw-r--r--thirdparty/openxr/include/openxr/openxr_reflection_structs.h76
-rw-r--r--thirdparty/openxr/patches/fix-gcc13-stdint.patch12
-rw-r--r--thirdparty/openxr/src/.clang-format10
-rw-r--r--thirdparty/openxr/src/common/extra_algorithms.h2
-rw-r--r--thirdparty/openxr/src/common/filesystem_utils.cpp2
-rw-r--r--thirdparty/openxr/src/common/filesystem_utils.hpp2
-rw-r--r--thirdparty/openxr/src/common/hex_and_handles.h2
-rw-r--r--thirdparty/openxr/src/common/loader_interfaces.h2
-rw-r--r--thirdparty/openxr/src/common/object_info.cpp6
-rw-r--r--thirdparty/openxr/src/common/object_info.h2
-rw-r--r--thirdparty/openxr/src/common/platform_utils.hpp43
-rw-r--r--thirdparty/openxr/src/common/stdfs_conditions.h2
-rw-r--r--thirdparty/openxr/src/common/unique_asset.h2
-rw-r--r--thirdparty/openxr/src/common/vulkan_debug_object_namer.hpp63
-rw-r--r--thirdparty/openxr/src/common/xr_dependencies.h32
-rw-r--r--thirdparty/openxr/src/common/xr_linear.h122
-rw-r--r--thirdparty/openxr/src/loader/android_utilities.cpp10
-rw-r--r--thirdparty/openxr/src/loader/android_utilities.h2
-rw-r--r--thirdparty/openxr/src/loader/api_layer_interface.cpp16
-rw-r--r--thirdparty/openxr/src/loader/api_layer_interface.hpp2
-rw-r--r--thirdparty/openxr/src/loader/exception_handling.hpp2
-rw-r--r--thirdparty/openxr/src/loader/loader_core.cpp2
-rw-r--r--thirdparty/openxr/src/loader/loader_instance.cpp6
-rw-r--r--thirdparty/openxr/src/loader/loader_instance.hpp2
-rw-r--r--thirdparty/openxr/src/loader/loader_logger.cpp5
-rw-r--r--thirdparty/openxr/src/loader/loader_logger.hpp2
-rw-r--r--thirdparty/openxr/src/loader/loader_logger_recorders.cpp12
-rw-r--r--thirdparty/openxr/src/loader/loader_logger_recorders.hpp2
-rw-r--r--thirdparty/openxr/src/loader/loader_platform.hpp2
-rw-r--r--thirdparty/openxr/src/loader/manifest_file.cpp15
-rw-r--r--thirdparty/openxr/src/loader/manifest_file.hpp2
-rw-r--r--thirdparty/openxr/src/loader/runtime_interface.cpp10
-rw-r--r--thirdparty/openxr/src/loader/runtime_interface.hpp2
-rw-r--r--thirdparty/openxr/src/loader/xr_generated_loader.cpp4
-rw-r--r--thirdparty/openxr/src/loader/xr_generated_loader.hpp4
-rw-r--r--thirdparty/openxr/src/xr_generated_dispatch_table.c81
-rw-r--r--thirdparty/openxr/src/xr_generated_dispatch_table.h80
1173 files changed, 39681 insertions, 22505 deletions
diff --git a/.github/actions/download-artifact/action.yml b/.github/actions/download-artifact/action.yml
new file mode 100644
index 0000000000..58c2aa9060
--- /dev/null
+++ b/.github/actions/download-artifact/action.yml
@@ -0,0 +1,18 @@
+name: Download Godot artifact
+description: Download the Godot artifact.
+inputs:
+ name:
+ description: The artifact name.
+ default: "${{ github.job }}"
+ path:
+ description: The path to download and extract to.
+ required: true
+ default: "./"
+runs:
+ using: "composite"
+ steps:
+ - name: Download Godot Artifact
+ uses: actions/download-artifact@v3
+ with:
+ name: ${{ inputs.name }}
+ path: ${{ inputs.path }}
diff --git a/.github/actions/godot-api-dump/action.yml b/.github/actions/godot-api-dump/action.yml
new file mode 100644
index 0000000000..47b675ae99
--- /dev/null
+++ b/.github/actions/godot-api-dump/action.yml
@@ -0,0 +1,24 @@
+name: Dump Godot API
+description: Dump Godot API for GDExtension
+inputs:
+ bin:
+ description: The path to the Godot executable
+ required: true
+runs:
+ using: "composite"
+ steps:
+ # Dump GDExtension interface and API
+ - name: Dump GDExtension interface and API for godot-cpp build
+ shell: sh
+ run: |
+ ${{ inputs.bin }} --headless --dump-gdextension-interface --dump-extension-api
+ mkdir godot-api
+ cp -f gdextension_interface.h godot-api/
+ cp -f extension_api.json godot-api/
+
+ - name: Upload API dump
+ uses: ./.github/actions/upload-artifact
+ with:
+ name: 'godot-api-dump'
+ path: './godot-api/*'
+
diff --git a/.github/actions/godot-cache/action.yml b/.github/actions/godot-cache/action.yml
index 2d7afc8514..09ad2099cc 100644
--- a/.github/actions/godot-cache/action.yml
+++ b/.github/actions/godot-cache/action.yml
@@ -16,7 +16,20 @@ runs:
with:
path: ${{inputs.scons-cache}}
key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
+
+ # We try to match an existing cache to restore from it. Each potential key is checked against
+ # all existing caches as a prefix. E.g. 'linux-template-minimal' would match any cache that
+ # starts with "linux-template-minimal", such as "linux-template-minimal-master-refs/heads/master-6588a4a29af1621086feac0117d5d4d37af957fd".
+ #
+ # We check these prefixes in this order:
+ #
+ # 1. The exact match, including the base branch, the commit reference, and the SHA hash of the commit.
+ # 2. A partial match for the same base branch and the same commit reference.
+ # 3. A partial match for the same base branch and the base branch commit reference.
+ # 4. A partial match for the same base branch only (not ideal, matches any PR with the same base branch).
+
restore-keys: |
${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
+ ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-refs/heads/${{env.GODOT_BASE_BRANCH}}
${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}
diff --git a/.github/actions/godot-converter-test/action.yml b/.github/actions/godot-converter-test/action.yml
new file mode 100644
index 0000000000..919a76e693
--- /dev/null
+++ b/.github/actions/godot-converter-test/action.yml
@@ -0,0 +1,18 @@
+name: Test Godot project converter
+description: Test the Godot project converter.
+inputs:
+ bin:
+ description: The path to the Godot executable
+ required: true
+runs:
+ using: "composite"
+ steps:
+ - name: Test 3-to-4 conversion
+ shell: sh
+ run: |
+ mkdir converter_test
+ cd converter_test
+ touch project.godot
+ ../${{ inputs.bin }} --headless --validate-conversion-3to4
+ cd ..
+ rm converter_test -rf
diff --git a/.github/actions/godot-project-test/action.yml b/.github/actions/godot-project-test/action.yml
new file mode 100644
index 0000000000..fd8c024a37
--- /dev/null
+++ b/.github/actions/godot-project-test/action.yml
@@ -0,0 +1,37 @@
+name: Test Godot project
+description: Run the test Godot project.
+inputs:
+ bin:
+ description: The path to the Godot executable
+ required: true
+runs:
+ using: "composite"
+ steps:
+ # Download and extract zip archive with project, folder is renamed to be able to easy change used project
+ - name: Download test project
+ shell: sh
+ run: |
+ wget https://github.com/godotengine/regression-test-project/archive/4.0.zip
+ unzip 4.0.zip
+ mv "regression-test-project-4.0" "test_project"
+
+ # Editor is quite complicated piece of software, so it is easy to introduce bug here.
+
+ - name: Open and close editor (Vulkan)
+ shell: sh
+ run: |
+ xvfb-run ${{ inputs.bin }} --audio-driver Dummy --editor --quit --path test_project 2>&1 | tee sanitizers_log.txt || true
+ misc/scripts/check_ci_log.py sanitizers_log.txt
+
+ - name: Open and close editor (GLES3)
+ shell: sh
+ run: |
+ DRI_PRIME=0 xvfb-run ${{ inputs.bin }} --audio-driver Dummy --rendering-driver opengl3 --editor --quit --path test_project 2>&1 | tee sanitizers_log.txt || true
+ misc/scripts/check_ci_log.py sanitizers_log.txt
+
+ # Run test project
+ - name: Run project
+ shell: sh
+ run: |
+ xvfb-run ${{ inputs.bin }} 40 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true
+ misc/scripts/check_ci_log.py sanitizers_log.txt
diff --git a/.github/workflows/godot_cpp_test.yml b/.github/workflows/godot_cpp_test.yml
new file mode 100644
index 0000000000..2051920f30
--- /dev/null
+++ b/.github/workflows/godot_cpp_test.yml
@@ -0,0 +1,54 @@
+name: 🪲 Godot CPP
+on:
+ workflow_call:
+
+# Global Settings
+env:
+ # Used for the cache key, and godot-cpp checkout. Add version suffix to force clean build.
+ GODOT_BASE_BRANCH: master
+
+concurrency:
+ group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-cpp-tests
+ cancel-in-progress: true
+
+jobs:
+ godot-cpp-tests:
+ runs-on: "ubuntu-20.04"
+ name: "Build and test Godot CPP"
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup python and scons
+ uses: ./.github/actions/godot-deps
+
+ # Checkout godot-cpp
+ - name: Checkout godot-cpp
+ uses: actions/checkout@v3
+ with:
+ repository: godotengine/godot-cpp
+ ref: ${{ env.GODOT_BASE_BRANCH }}
+ submodules: 'recursive'
+ path: 'godot-cpp'
+
+ # Download generated API dump
+ - name: Download GDExtension interface and API dump
+ uses: ./.github/actions/download-artifact
+ with:
+ name: 'godot-api-dump'
+ path: './godot-api'
+
+ # Extract and override existing files with generated files
+ - name: Extract GDExtension interface and API dump
+ run: |
+ cp -f godot-api/gdextension_interface.h godot-cpp/gdextension/
+ cp -f godot-api/extension_api.json godot-cpp/gdextension/
+
+ # TODO: Add caching to the scons build and store it for CI via the godot-cache
+ # action.
+
+ # Build godot-cpp test extension
+ - name: Build godot-cpp test extension
+ run: |
+ cd godot-cpp/test
+ scons target=template_debug dev_build=yes
+ cd ../..
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index aef8f83a53..e5ab2fde43 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -4,7 +4,7 @@ on:
# Global Settings
env:
- # Used for the cache key, and godot-cpp checkout. Add version suffix to force clean build.
+ # Used for the cache key. Add version suffix to force clean build.
GODOT_BASE_BRANCH: master
SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes
DOTNET_NOLOGO: true
@@ -25,53 +25,52 @@ jobs:
- name: Editor w/ Mono (target=editor)
cache-name: linux-editor-mono
target: editor
- tests: false # Disabled due freeze caused by mix Mono build and CI
sconsflags: module_mono_enabled=yes
- doc-test: true
bin: "./bin/godot.linuxbsd.editor.x86_64.mono"
build-mono: true
+ tests: false # Disabled due freeze caused by mix Mono build and CI
+ doc-test: true
proj-conv: true
+ api-compat: true
artifact: true
- compat: true
- name: Editor with doubles and GCC sanitizers (target=editor, tests=yes, dev_build=yes, scu_build=yes, precision=double, use_asan=yes, use_ubsan=yes, linker=gold)
cache-name: linux-editor-double-sanitizers
target: editor
- tests: true
# Debug symbols disabled as they're huge on this build and we hit the 14 GB limit for runners.
sconsflags: dev_build=yes scu_build=yes debug_symbols=no precision=double use_asan=yes use_ubsan=yes linker=gold
- proj-test: true
- # Can be turned off for PRs that intentionally break compat with godot-cpp,
- # until both the upstream PR and the matching godot-cpp changes are merged.
- godot-cpp-test: true
bin: "./bin/godot.linuxbsd.editor.dev.double.x86_64.san"
build-mono: false
+ tests: true
+ proj-test: true
+ # Generate an API dump for godot-cpp tests.
+ api-dump: true
# Skip 2GiB artifact speeding up action.
artifact: false
- name: Editor with clang sanitizers (target=editor, tests=yes, dev_build=yes, use_asan=yes, use_ubsan=yes, use_llvm=yes, linker=lld)
cache-name: linux-editor-llvm-sanitizers
target: editor
- tests: true
sconsflags: dev_build=yes use_asan=yes use_ubsan=yes use_llvm=yes linker=lld
bin: "./bin/godot.linuxbsd.editor.dev.x86_64.llvm.san"
build-mono: false
+ tests: true
# Skip 2GiB artifact speeding up action.
artifact: false
- name: Template w/ Mono (target=template_release)
cache-name: linux-template-mono
target: template_release
- tests: false
sconsflags: module_mono_enabled=yes
build-mono: false
+ tests: false
artifact: true
- name: Minimal template (target=template_release, everything disabled)
cache-name: linux-template-minimal
target: template_release
- tests: false
sconsflags: modules_enabled_by_default=no disable_3d=yes disable_advanced_gui=yes deprecated=no minizip=no
+ tests: false
artifact: true
steps:
@@ -85,6 +84,12 @@ jobs:
sudo add-apt-repository ppa:kisak/kisak-mesa
sudo apt-get install -qq mesa-vulkan-drivers
+ - name: Free disk space on runner
+ run: |
+ echo "Disk usage before:" && df -h
+ sudo rm -rf /usr/local/lib/android
+ echo "Disk usage after:" && df -h
+
- name: Setup Godot build cache
uses: ./.github/actions/godot-cache
with:
@@ -121,6 +126,24 @@ jobs:
run: |
./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=linuxbsd
+ - name: Prepare artifact
+ if: ${{ matrix.artifact }}
+ run: |
+ strip bin/godot.*
+ chmod +x bin/godot.*
+
+ - name: Upload artifact
+ uses: ./.github/actions/upload-artifact
+ if: ${{ matrix.artifact }}
+ with:
+ name: ${{ matrix.cache-name }}
+
+ - name: Dump Godot API
+ uses: ./.github/actions/godot-api-dump
+ if: ${{ matrix.api-dump }}
+ with:
+ bin: ${{ matrix.bin }}
+
# Execute unit tests for the editor
- name: Unit tests
if: ${{ matrix.tests }}
@@ -138,84 +161,22 @@ jobs:
${{ matrix.bin }} --doctool --headless 2>&1 > /dev/null || true
git diff --color --exit-code && ! git ls-files --others --exclude-standard | sed -e 's/^/New doc file missing in PR: /' | grep 'xml$'
- # Test 3.x -> 4.x project converter
- - name: Test project converter
- if: ${{ matrix.proj-conv }}
- run: |
- mkdir converter_test
- cd converter_test
- touch project.godot
- ../${{ matrix.bin }} --headless --validate-conversion-3to4
- cd ..
- rm converter_test -rf
-
- # Download and extract zip archive with project, folder is renamed to be able to easy change used project
- - name: Download test project
- if: ${{ matrix.proj-test }}
- run: |
- wget https://github.com/godotengine/regression-test-project/archive/4.0.zip
- unzip 4.0.zip
- mv "regression-test-project-4.0" "test_project"
-
- # Editor is quite complicated piece of software, so it is easy to introduce bug here
- - name: Open and close editor (Vulkan)
- if: ${{ matrix.proj-test }}
- run: |
- xvfb-run ${{ matrix.bin }} --audio-driver Dummy --editor --quit --path test_project 2>&1 | tee sanitizers_log.txt || true
- misc/scripts/check_ci_log.py sanitizers_log.txt
-
- - name: Open and close editor (GLES3)
- if: ${{ matrix.proj-test }}
- run: |
- DRI_PRIME=0 xvfb-run ${{ matrix.bin }} --audio-driver Dummy --rendering-driver opengl3 --editor --quit --path test_project 2>&1 | tee sanitizers_log.txt || true
- misc/scripts/check_ci_log.py sanitizers_log.txt
-
- # Run test project
- - name: Run project
- if: ${{ matrix.proj-test }}
- run: |
- xvfb-run ${{ matrix.bin }} 40 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true
- misc/scripts/check_ci_log.py sanitizers_log.txt
-
- # Checkout godot-cpp
- - name: Checkout godot-cpp
- if: ${{ matrix.godot-cpp-test }}
- uses: actions/checkout@v3
- with:
- repository: godotengine/godot-cpp
- ref: ${{ env.GODOT_BASE_BRANCH }}
- submodules: 'recursive'
- path: 'godot-cpp'
-
- # Dump GDExtension interface and API
- - name: Dump GDExtension interface and API for godot-cpp build
- if: ${{ matrix.godot-cpp-test }}
- run: |
- ${{ matrix.bin }} --headless --dump-gdextension-interface --dump-extension-api
- cp -f gdextension_interface.h godot-cpp/gdextension/
- cp -f extension_api.json godot-cpp/gdextension/
-
- # Build godot-cpp test extension
- - name: Build godot-cpp test extension
- if: ${{ matrix.godot-cpp-test }}
- run: |
- cd godot-cpp/test
- scons target=template_debug dev_build=yes
- cd ../..
-
+ # Check API backwards compatibility
- name: Check for GDExtension compatibility
- if: ${{ matrix.compat }}
+ if: ${{ matrix.api-compat }}
run: |
./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}" || true # don't fail the CI for now
- - name: Prepare artifact
- if: ${{ matrix.artifact }}
- run: |
- strip bin/godot.*
- chmod +x bin/godot.*
+ # Download and run the test project
+ - name: Test Godot project
+ uses: ./.github/actions/godot-project-test
+ if: ${{ matrix.proj-test }}
+ with:
+ bin: ${{ matrix.bin }}
- - name: Upload artifact
- uses: ./.github/actions/upload-artifact
- if: ${{ matrix.artifact }}
+ # Test the project converter
+ - name: Test project converter
+ uses: ./.github/actions/godot-converter-test
+ if: ${{ matrix.proj-conv }}
with:
- name: ${{ matrix.cache-name }}
+ bin: ${{ matrix.bin }}
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml
index 6e0fbbf461..ae6f452bc2 100644
--- a/.github/workflows/macos_builds.yml
+++ b/.github/workflows/macos_builds.yml
@@ -56,14 +56,6 @@ jobs:
target: ${{ matrix.target }}
tests: ${{ matrix.tests }}
- # Execute unit tests for the editor
- - name: Unit tests
- if: ${{ matrix.tests }}
- run: |
- ${{ matrix.bin }} --version
- ${{ matrix.bin }} --help
- ${{ matrix.bin }} --test
-
- name: Prepare artifact
run: |
strip bin/godot.*
@@ -73,3 +65,11 @@ jobs:
uses: ./.github/actions/upload-artifact
with:
name: ${{ matrix.cache-name }}
+
+ # Execute unit tests for the editor
+ - name: Unit tests
+ if: ${{ matrix.tests }}
+ run: |
+ ${{ matrix.bin }} --version
+ ${{ matrix.bin }} --help
+ ${{ matrix.bin }} --test
diff --git a/.github/workflows/runner.yml b/.github/workflows/runner.yml
index be255b5468..8e1741e844 100644
--- a/.github/workflows/runner.yml
+++ b/.github/workflows/runner.yml
@@ -6,10 +6,14 @@ concurrency:
cancel-in-progress: true
jobs:
+ # First stage: Only static checks, fast and prevent expensive builds from running.
+
static-checks:
name: 📊 Static checks
uses: ./.github/workflows/static_checks.yml
+ # Second stage: Run all the builds and some of the tests.
+
android-build:
name: 🤖 Android
needs: static-checks
@@ -39,3 +43,15 @@ jobs:
name: 🌐 Web
needs: static-checks
uses: ./.github/workflows/web_builds.yml
+
+ # Third stage: Run auxiliary tests using build artifacts from previous jobs.
+
+ # Can be turned off for PRs that intentionally break compat with godot-cpp,
+ # until both the upstream PR and the matching godot-cpp changes are merged.
+ godot-cpp-test:
+ name: 🪲 Godot CPP
+ # This can be changed to depend on another platform, if we decide to use it for
+ # godot-cpp instead. Make sure to move the .github/actions/godot-api-dump step
+ # appropriately.
+ needs: linux-build
+ uses: ./.github/workflows/godot_cpp_test.yml
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/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
index 182ae2fc8c..ab62dca5cb 100644
--- a/.github/workflows/windows_builds.yml
+++ b/.github/workflows/windows_builds.yml
@@ -60,14 +60,6 @@ jobs:
target: ${{ matrix.target }}
tests: ${{ matrix.tests }}
- # Execute unit tests for the editor
- - name: Unit tests
- if: ${{ matrix.tests }}
- run: |
- ${{ matrix.bin }} --version
- ${{ matrix.bin }} --help
- ${{ matrix.bin }} --test
-
- name: Prepare artifact
run: |
Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force
@@ -76,3 +68,11 @@ jobs:
uses: ./.github/actions/upload-artifact
with:
name: ${{ matrix.cache-name }}
+
+ # Execute unit tests for the editor
+ - name: Unit tests
+ if: ${{ matrix.tests }}
+ run: |
+ ${{ matrix.bin }} --version
+ ${{ matrix.bin }} --help
+ ${{ matrix.bin }} --test
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97850763a2..735cca7212 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -161,6 +161,7 @@ See the [release announcement](https://godotengine.org/article/godot-4-1-is-here
#### Core
+- The strings returned by `ResourceLoader::get_dependencies()` now include paths in addition to UIDs ([GH-73131](https://github.com/godotengine/godot/pull/73131)).
- Optimize Node children management ([GH-75627](https://github.com/godotengine/godot/pull/75627)).
- Deprecate `NOTIFICATION_MOVED_IN_PARENT` for `NOTIFICATION_CHILD_ORDER_CHANGED` ([GH-75701](https://github.com/godotengine/godot/pull/75701)).
- Optimize `Node::add_child` validation ([GH-75760](https://github.com/godotengine/godot/pull/75760)).
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 9f01e9c414..582784d78e 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -409,7 +409,7 @@ License: Apache-2.0
Files: ./thirdparty/openxr/
Comment: OpenXR Loader
-Copyright: 2020-2022, The Khronos Group Inc.
+Copyright: 2020-2023, The Khronos Group Inc.
License: Apache-2.0
Files: ./thirdparty/pcre2/
diff --git a/SConstruct b/SConstruct
index 7e706e8e73..f82c9c656e 100644
--- a/SConstruct
+++ b/SConstruct
@@ -240,6 +240,7 @@ opts.Add(BoolVariable("builtin_libwebp", "Use the built-in libwebp library", Tru
opts.Add(BoolVariable("builtin_wslay", "Use the built-in wslay library", True))
opts.Add(BoolVariable("builtin_mbedtls", "Use the built-in mbedTLS library", True))
opts.Add(BoolVariable("builtin_miniupnpc", "Use the built-in miniupnpc library", True))
+opts.Add(BoolVariable("builtin_openxr", "Use the built-in OpenXR library", True))
opts.Add(BoolVariable("builtin_pcre2", "Use the built-in PCRE2 library", True))
opts.Add(BoolVariable("builtin_pcre2_with_jit", "Use JIT compiler for the built-in PCRE2 library", True))
opts.Add(BoolVariable("builtin_recastnavigation", "Use the built-in Recast navigation library", True))
@@ -297,21 +298,21 @@ else:
if selected_platform in ["macos", "osx"]:
if selected_platform == "osx":
# Deprecated alias kept for compatibility.
- print('Platform "osx" has been renamed to "macos" in Godot 4.0. Building for platform "macos".')
+ print('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".')
# Alias for convenience.
selected_platform = "macos"
if selected_platform in ["ios", "iphone"]:
if selected_platform == "iphone":
# Deprecated alias kept for compatibility.
- print('Platform "iphone" has been renamed to "ios" in Godot 4.0. Building for platform "ios".')
+ print('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".')
# Alias for convenience.
selected_platform = "ios"
if selected_platform in ["linux", "bsd", "x11"]:
if selected_platform == "x11":
# Deprecated alias kept for compatibility.
- print('Platform "x11" has been renamed to "linuxbsd" in Godot 4.0. Building for platform "linuxbsd".')
+ print('Platform "x11" has been renamed to "linuxbsd" in Godot 4. Building for platform "linuxbsd".')
# Alias for convenience.
selected_platform = "linuxbsd"
diff --git a/core/SCsub b/core/SCsub
index 7e9cd97351..ab78eeedc7 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -97,7 +97,6 @@ if env["builtin_zlib"]:
"compress.c",
"crc32.c",
"deflate.c",
- "infback.c",
"inffast.c",
"inflate.c",
"inftrees.c",
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 6b25510b14..dc0b2a1cf5 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -230,14 +230,16 @@ public:
String get_cache_dir() const;
Error set_thread_name(const String &p_name);
- Thread::ID get_thread_caller_id() const;
- Thread::ID get_main_thread_id() const;
+ ::Thread::ID get_thread_caller_id() const;
+ ::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/debugger/debugger_marshalls.cpp b/core/debugger/debugger_marshalls.cpp
index 591b44869f..3e6b7501c7 100644
--- a/core/debugger/debugger_marshalls.cpp
+++ b/core/debugger/debugger_marshalls.cpp
@@ -67,6 +67,7 @@ Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) {
Array arr;
arr.push_back(name);
arr.push_back(type);
+ arr.push_back(value.get_type());
Variant var = value;
if (value.get_type() == Variant::OBJECT && value.get_validated_object() == nullptr) {
@@ -74,7 +75,7 @@ Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) {
}
int len = 0;
- Error err = encode_variant(var, nullptr, len, true);
+ Error err = encode_variant(var, nullptr, len, false);
if (err != OK) {
ERR_PRINT("Failed to encode variant.");
}
@@ -88,11 +89,12 @@ Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) {
}
bool DebuggerMarshalls::ScriptStackVariable::deserialize(const Array &p_arr) {
- CHECK_SIZE(p_arr, 3, "ScriptStackVariable");
+ CHECK_SIZE(p_arr, 4, "ScriptStackVariable");
name = p_arr[0];
type = p_arr[1];
- value = p_arr[2];
- CHECK_END(p_arr, 3, "ScriptStackVariable");
+ var_type = p_arr[2];
+ value = p_arr[3];
+ CHECK_END(p_arr, 4, "ScriptStackVariable");
return true;
}
diff --git a/core/debugger/debugger_marshalls.h b/core/debugger/debugger_marshalls.h
index 8ba93c3092..1b81623688 100644
--- a/core/debugger/debugger_marshalls.h
+++ b/core/debugger/debugger_marshalls.h
@@ -38,6 +38,7 @@ struct DebuggerMarshalls {
String name;
Variant value;
int type = -1;
+ int var_type = -1;
Array serialize(int max_size = 1 << 20); // 1 MiB default.
bool deserialize(const Array &p_arr);
diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp
index 6c9293a2cf..32dc060aa2 100644
--- a/core/debugger/engine_debugger.cpp
+++ b/core/debugger/engine_debugger.cpp
@@ -111,14 +111,6 @@ Error EngineDebugger::capture_parse(const StringName &p_name, const String &p_ms
return cap.capture(cap.data, p_msg, p_args, r_captured);
}
-void EngineDebugger::line_poll() {
- // The purpose of this is just processing events every now and then when the script might get too busy otherwise bugs like infinite loops can't be caught
- if (poll_every % 2048 == 0) {
- poll_events(false);
- }
- poll_every++;
-}
-
void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, double p_physics_frame_time) {
frame_time = USEC_TO_SEC(p_frame_ticks);
process_time = USEC_TO_SEC(p_process_ticks);
diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h
index 1bae71e37a..88d5490794 100644
--- a/core/debugger/engine_debugger.h
+++ b/core/debugger/engine_debugger.h
@@ -126,7 +126,13 @@ public:
void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array());
Error capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured);
- void line_poll();
+ void line_poll() {
+ // The purpose of this is just processing events every now and then when the script might get too busy otherwise bugs like infinite loops can't be caught.
+ if (unlikely(poll_every % 2048) == 0) {
+ poll_events(false);
+ }
+ poll_every++;
+ }
virtual void poll_events(bool p_is_idle) {}
virtual void send_message(const String &p_msg, const Array &p_data) = 0;
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index b7471d7c82..b4d6fa4174 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -94,6 +94,7 @@ public:
Error RemoteDebugger::_put_msg(String p_message, Array p_data) {
Array msg;
msg.push_back(p_message);
+ msg.push_back(Thread::get_caller_id());
msg.push_back(p_data);
Error err = peer->put_message(msg);
if (err != OK) {
@@ -185,9 +186,9 @@ RemoteDebugger::ErrorMessage RemoteDebugger::_create_overflow_error(const String
}
void RemoteDebugger::flush_output() {
+ MutexLock lock(mutex);
flush_thread = Thread::get_caller_id();
flushing = true;
- MutexLock lock(mutex);
if (!is_peer_connected()) {
return;
}
@@ -348,18 +349,65 @@ Error RemoteDebugger::_try_capture(const String &p_msg, const Array &p_data, boo
return capture_parse(cap, msg, p_data, r_captured);
}
+void RemoteDebugger::_poll_messages() {
+ MutexLock mutex_lock(mutex);
+
+ peer->poll();
+ while (peer->has_message()) {
+ Array cmd = peer->get_message();
+ ERR_CONTINUE(cmd.size() != 3);
+ ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
+ ERR_CONTINUE(cmd[1].get_type() != Variant::INT);
+ ERR_CONTINUE(cmd[2].get_type() != Variant::ARRAY);
+
+ Thread::ID thread = cmd[1];
+
+ if (!messages.has(thread)) {
+ continue; // This thread is not around to receive the messages
+ }
+
+ Message msg;
+ msg.message = cmd[0];
+ msg.data = cmd[2];
+ messages[thread].push_back(msg);
+ }
+}
+
+bool RemoteDebugger::_has_messages() {
+ MutexLock mutex_lock(mutex);
+ return messages.has(Thread::get_caller_id()) && !messages[Thread::get_caller_id()].is_empty();
+}
+
+Array RemoteDebugger::_get_message() {
+ MutexLock mutex_lock(mutex);
+ ERR_FAIL_COND_V(!messages.has(Thread::get_caller_id()), Array());
+ List<Message> &message_list = messages[Thread::get_caller_id()];
+ ERR_FAIL_COND_V(message_list.is_empty(), Array());
+
+ Array msg;
+ msg.resize(2);
+ msg[0] = message_list.front()->get().message;
+ msg[1] = message_list.front()->get().data;
+ message_list.pop_front();
+ return msg;
+}
+
void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
//this function is called when there is a debugger break (bug on script)
//or when execution is paused from editor
- if (script_debugger->is_skipping_breakpoints() && !p_is_error_breakpoint) {
- return;
- }
+ {
+ MutexLock lock(mutex);
+ // Tests that require mutex.
+ if (script_debugger->is_skipping_breakpoints() && !p_is_error_breakpoint) {
+ return;
+ }
- ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway.");
+ ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway.");
- if (!peer->can_block()) {
- return; // Peer does not support blocking IO. We could at least send the error though.
+ if (!peer->can_block()) {
+ return; // Peer does not support blocking IO. We could at least send the error though.
+ }
}
ScriptLanguage *script_lang = script_debugger->get_break_language();
@@ -369,22 +417,33 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
msg.push_back(error_str);
ERR_FAIL_COND(!script_lang);
msg.push_back(script_lang->debug_get_stack_level_count() > 0);
+ msg.push_back(Thread::get_caller_id() == Thread::get_main_id() ? String(RTR("Main Thread")) : itos(Thread::get_caller_id()));
if (allow_focus_steal_fn) {
allow_focus_steal_fn();
}
send_message("debug_enter", msg);
- Input::MouseMode mouse_mode = Input::get_singleton()->get_mouse_mode();
- if (mouse_mode != Input::MOUSE_MODE_VISIBLE) {
- Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ Input::MouseMode mouse_mode = Input::MOUSE_MODE_VISIBLE;
+
+ if (Thread::get_caller_id() == Thread::get_main_id()) {
+ mouse_mode = Input::get_singleton()->get_mouse_mode();
+ if (mouse_mode != Input::MOUSE_MODE_VISIBLE) {
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ }
+ } else {
+ MutexLock mutex_lock(mutex);
+ messages.insert(Thread::get_caller_id(), List<Message>());
}
+ mutex.lock();
while (is_peer_connected()) {
+ mutex.unlock();
flush_output();
- peer->poll();
- if (peer->has_message()) {
- Array cmd = peer->get_message();
+ _poll_messages();
+
+ if (_has_messages()) {
+ Array cmd = _get_message();
ERR_CONTINUE(cmd.size() != 2);
ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
@@ -479,14 +538,22 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
}
} else {
OS::get_singleton()->delay_usec(10000);
- OS::get_singleton()->process_and_drop_events();
+ if (Thread::get_caller_id() == Thread::get_main_id()) {
+ // If this is a busy loop on the main thread, events still need to be processed.
+ OS::get_singleton()->process_and_drop_events();
+ }
}
}
send_message("debug_exit", Array());
- if (mouse_mode != Input::MOUSE_MODE_VISIBLE) {
- Input::get_singleton()->set_mouse_mode(mouse_mode);
+ if (Thread::get_caller_id() == Thread::get_main_id()) {
+ if (mouse_mode != Input::MOUSE_MODE_VISIBLE) {
+ Input::get_singleton()->set_mouse_mode(mouse_mode);
+ }
+ } else {
+ MutexLock mutex_lock(mutex);
+ messages.erase(Thread::get_caller_id());
}
}
@@ -496,9 +563,11 @@ void RemoteDebugger::poll_events(bool p_is_idle) {
}
flush_output();
- peer->poll();
- while (peer->has_message()) {
- Array arr = peer->get_message();
+
+ _poll_messages();
+
+ while (_has_messages()) {
+ Array arr = _get_message();
ERR_CONTINUE(arr.size() != 2);
ERR_CONTINUE(arr[0].get_type() != Variant::STRING);
@@ -604,6 +673,8 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) {
eh.errfunc = _err_handler;
eh.userdata = this;
add_error_handler(&eh);
+
+ messages.insert(Thread::get_main_id(), List<Message>());
}
RemoteDebugger::~RemoteDebugger() {
diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h
index 24283b0ed6..7c399178c6 100644
--- a/core/debugger/remote_debugger.h
+++ b/core/debugger/remote_debugger.h
@@ -80,6 +80,17 @@ private:
bool flushing = false;
Thread::ID flush_thread = 0;
+ struct Message {
+ String message;
+ Array data;
+ };
+
+ HashMap<Thread::ID, List<Message>> messages;
+
+ void _poll_messages();
+ bool _has_messages();
+ Array _get_message();
+
PrintHandlerList phl;
static void _print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich);
ErrorHandlerList eh;
diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp
index 32725b76c1..e7d8654a0b 100644
--- a/core/debugger/script_debugger.cpp
+++ b/core/debugger/script_debugger.cpp
@@ -32,22 +32,19 @@
#include "core/debugger/engine_debugger.h"
+thread_local int ScriptDebugger::lines_left = -1;
+thread_local int ScriptDebugger::depth = -1;
+thread_local ScriptLanguage *ScriptDebugger::break_lang = nullptr;
+thread_local Vector<ScriptDebugger::StackInfo> ScriptDebugger::error_stack_info;
+
void ScriptDebugger::set_lines_left(int p_left) {
lines_left = p_left;
}
-int ScriptDebugger::get_lines_left() const {
- return lines_left;
-}
-
void ScriptDebugger::set_depth(int p_depth) {
depth = p_depth;
}
-int ScriptDebugger::get_depth() const {
- return depth;
-}
-
void ScriptDebugger::insert_breakpoint(int p_line, const StringName &p_source) {
if (!breakpoints.has(p_line)) {
breakpoints[p_line] = HashSet<StringName>();
@@ -66,13 +63,6 @@ void ScriptDebugger::remove_breakpoint(int p_line, const StringName &p_source) {
}
}
-bool ScriptDebugger::is_breakpoint(int p_line, const StringName &p_source) const {
- if (!breakpoints.has(p_line)) {
- return false;
- }
- return breakpoints[p_line].has(p_source);
-}
-
String ScriptDebugger::breakpoint_find_source(const String &p_source) const {
return p_source;
}
@@ -100,7 +90,7 @@ void ScriptDebugger::send_error(const String &p_func, const String &p_file, int
// Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way.
error_stack_info.append_array(p_stack_info);
EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type);
- error_stack_info.clear();
+ error_stack_info.clear(); // Clear because this is thread local
}
Vector<ScriptLanguage::StackInfo> ScriptDebugger::get_error_stack_info() const {
diff --git a/core/debugger/script_debugger.h b/core/debugger/script_debugger.h
index edce089179..ee037b91fa 100644
--- a/core/debugger/script_debugger.h
+++ b/core/debugger/script_debugger.h
@@ -40,21 +40,25 @@
class ScriptDebugger {
typedef ScriptLanguage::StackInfo StackInfo;
- int lines_left = -1;
- int depth = -1;
bool skip_breakpoints = false;
HashMap<int, HashSet<StringName>> breakpoints;
- ScriptLanguage *break_lang = nullptr;
- Vector<StackInfo> error_stack_info;
+ static thread_local int lines_left;
+ static thread_local int depth;
+ static thread_local ScriptLanguage *break_lang;
+ static thread_local Vector<StackInfo> error_stack_info;
public:
void set_lines_left(int p_left);
- int get_lines_left() const;
+ _ALWAYS_INLINE_ int get_lines_left() const {
+ return lines_left;
+ }
void set_depth(int p_depth);
- int get_depth() const;
+ _ALWAYS_INLINE_ int get_depth() const {
+ return depth;
+ }
String breakpoint_find_source(const String &p_source) const;
void set_break_language(ScriptLanguage *p_lang) { break_lang = p_lang; }
@@ -63,7 +67,12 @@ public:
bool is_skipping_breakpoints();
void insert_breakpoint(int p_line, const StringName &p_source);
void remove_breakpoint(int p_line, const StringName &p_source);
- bool is_breakpoint(int p_line, const StringName &p_source) const;
+ _ALWAYS_INLINE_ bool is_breakpoint(int p_line, const StringName &p_source) const {
+ if (likely(!breakpoints.has(p_line))) {
+ return false;
+ }
+ return breakpoints[p_line].has(p_source);
+ }
void clear_breakpoints();
const HashMap<int, HashSet<StringName>> &get_breakpoints() const { return breakpoints; }
diff --git a/core/doc_data.h b/core/doc_data.h
index 0fe7414b98..b8c92a4b67 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -532,6 +532,42 @@ public:
}
};
+ struct EnumDoc {
+ String description;
+ bool is_deprecated = false;
+ bool is_experimental = false;
+ static EnumDoc from_dict(const Dictionary &p_dict) {
+ EnumDoc doc;
+
+ if (p_dict.has("description")) {
+ doc.description = p_dict["description"];
+ }
+
+ if (p_dict.has("is_deprecated")) {
+ doc.is_deprecated = p_dict["is_deprecated"];
+ }
+
+ if (p_dict.has("is_experimental")) {
+ doc.is_experimental = p_dict["is_experimental"];
+ }
+
+ return doc;
+ }
+ static Dictionary to_dict(const EnumDoc &p_doc) {
+ Dictionary dict;
+
+ if (!p_doc.description.is_empty()) {
+ dict["description"] = p_doc.description;
+ }
+
+ dict["is_deprecated"] = p_doc.is_deprecated;
+
+ dict["is_experimental"] = p_doc.is_experimental;
+
+ return dict;
+ }
+ };
+
struct ClassDoc {
String name;
String inherits;
@@ -543,7 +579,7 @@ public:
Vector<MethodDoc> operators;
Vector<MethodDoc> signals;
Vector<ConstantDoc> constants;
- HashMap<String, String> enums;
+ HashMap<String, EnumDoc> enums;
Vector<PropertyDoc> properties;
Vector<MethodDoc> annotations;
Vector<ThemeItemDoc> theme_properties;
@@ -626,7 +662,7 @@ public:
enums = p_dict["enums"];
}
for (int i = 0; i < enums.size(); i++) {
- doc.enums[enums.get_key_at_index(i)] = enums.get_value_at_index(i);
+ doc.enums[enums.get_key_at_index(i)] = EnumDoc::from_dict(enums.get_value_at_index(i));
}
Array properties;
@@ -740,8 +776,8 @@ public:
if (!p_doc.enums.is_empty()) {
Dictionary enums;
- for (const KeyValue<String, String> &E : p_doc.enums) {
- enums[E.key] = E.value;
+ for (const KeyValue<String, EnumDoc> &E : p_doc.enums) {
+ enums[E.key] = EnumDoc::to_dict(E.value);
}
dict["enums"] = enums;
}
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 73526fae3e..67b55db3db 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -363,6 +363,10 @@ void GDExtension::_register_extension_class_integer_constant(GDExtensionClassLib
}
void GDExtension::_register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter) {
+ _register_extension_class_property_indexed(p_library, p_class_name, p_info, p_setter, p_getter, -1);
+}
+
+void GDExtension::_register_extension_class_property_indexed(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
@@ -371,10 +375,9 @@ void GDExtension::_register_extension_class_property(GDExtensionClassLibraryPtr
String property_name = *reinterpret_cast<const StringName *>(p_info->name);
ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property '" + property_name + "' for unexisting class '" + class_name + "'.");
- //Extension *extension = &self->extension_classes[class_name];
PropertyInfo pinfo(*p_info);
- ClassDB::add_property(class_name, pinfo, setter, getter);
+ ClassDB::add_property(class_name, pinfo, setter, getter, p_index);
}
void GDExtension::_register_extension_class_property_group(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringPtr p_group_name, GDExtensionConstStringPtr p_prefix) {
@@ -542,6 +545,7 @@ void GDExtension::initialize_gdextensions() {
register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
register_interface_function("classdb_register_extension_class_property", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property);
+ register_interface_function("classdb_register_extension_class_property_indexed", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_indexed);
register_interface_function("classdb_register_extension_class_property_group", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_group);
register_interface_function("classdb_register_extension_class_property_subgroup", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_subgroup);
register_interface_function("classdb_register_extension_class_signal", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_signal);
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 77ec458d30..b935f8706f 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -54,6 +54,7 @@ class GDExtension : public Resource {
static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
static void _register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter);
+ static void _register_extension_class_property_indexed(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index);
static void _register_extension_class_property_group(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_group_name, GDExtensionConstStringNamePtr p_prefix);
static void _register_extension_class_property_subgroup(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_subgroup_name, GDExtensionConstStringNamePtr p_prefix);
static void _register_extension_class_signal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_signal_name, const GDExtensionPropertyInfo *p_argument_info, GDExtensionInt p_argument_count);
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 7fbf2d00a1..7ef956a470 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -726,6 +726,11 @@ static void gdextension_string_operator_plus_eq_c32str(GDExtensionStringPtr p_se
*self += p_b;
}
+static GDExtensionInt gdextension_string_resize(GDExtensionStringPtr p_self, GDExtensionInt p_length) {
+ String *self = (String *)p_self;
+ return (*self).resize(p_length);
+}
+
static GDExtensionInt gdextension_xml_parser_open_buffer(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size) {
XMLParser *xml = (XMLParser *)p_instance;
return (GDExtensionInt)xml->_open_buffer(p_buffer, p_size);
@@ -1167,6 +1172,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(string_operator_plus_eq_cstr);
REGISTER_INTERFACE_FUNC(string_operator_plus_eq_wcstr);
REGISTER_INTERFACE_FUNC(string_operator_plus_eq_c32str);
+ REGISTER_INTERFACE_FUNC(string_resize);
REGISTER_INTERFACE_FUNC(xml_parser_open_buffer);
REGISTER_INTERFACE_FUNC(file_access_store_buffer);
REGISTER_INTERFACE_FUNC(file_access_get_buffer);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 4d7bdf9502..6c05f3988b 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -1526,6 +1526,25 @@ typedef void (*GDExtensionInterfaceStringOperatorPlusEqWcstr)(GDExtensionStringP
*/
typedef void (*GDExtensionInterfaceStringOperatorPlusEqC32str)(GDExtensionStringPtr p_self, const char32_t *p_b);
+/**
+ * @name string_resize
+ * @since 4.2
+ *
+ * Resizes the underlying string data to the given number of characters.
+ *
+ * Space needs to be allocated for the null terminating character ('\0') which
+ * also must be added manually, in order for all string functions to work correctly.
+ *
+ * Warning: This is an error-prone operation - only use it if there's no other
+ * efficient way to accomplish your goal.
+ *
+ * @param p_self A pointer to the String.
+ * @param p_resize The new length for the String.
+ *
+ * @return Error code signifying if the operation successful.
+ */
+typedef GDExtensionInt (*GDExtensionInterfaceStringResize)(GDExtensionStringPtr p_self, GDExtensionInt p_resize);
+
/* INTERFACE: XMLParser Utilities */
/**
@@ -2212,6 +2231,23 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant)
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassProperty)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter);
/**
+ * @name classdb_register_extension_class_property_indexed
+ * @since 4.2
+ *
+ * Registers an indexed property on an extension class in the ClassDB.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_info A pointer to a GDExtensionPropertyInfo struct.
+ * @param p_setter A pointer to a StringName with the name of the setter method.
+ * @param p_getter A pointer to a StringName with the name of the getter method.
+ * @param p_index The index to pass as the first argument to the getter and setter methods.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassPropertyIndexed)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index);
+
+/**
* @name classdb_register_extension_class_property_group
* @since 4.1
*
diff --git a/core/input/input.cpp b/core/input/input.cpp
index cf8d71b9a7..d481acf005 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -113,6 +113,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &Input::get_joy_axis);
ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &Input::get_joy_name);
ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &Input::get_joy_guid);
+ ClassDB::bind_method(D_METHOD("should_ignore_device", "vendor_id", "product_id"), &Input::should_ignore_device);
ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads);
ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength);
ClassDB::bind_method(D_METHOD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration);
@@ -1498,6 +1499,11 @@ String Input::get_joy_guid(int p_device) const {
return joy_names[p_device].uid;
}
+bool Input::should_ignore_device(int p_vendor_id, int p_product_id) const {
+ uint32_t full_id = (((uint32_t)p_vendor_id) << 16) | ((uint16_t)p_product_id);
+ return ignored_device_ids.has(full_id);
+}
+
TypedArray<int> Input::get_connected_joypads() {
TypedArray<int> ret;
HashMap<int, Joypad>::Iterator elem = joy_names.begin();
@@ -1542,6 +1548,27 @@ Input::Input() {
}
}
+ String env_ignore_devices = OS::get_singleton()->get_environment("SDL_GAMECONTROLLER_IGNORE_DEVICES");
+ if (!env_ignore_devices.is_empty()) {
+ Vector<String> entries = env_ignore_devices.split(",");
+ for (int i = 0; i < entries.size(); i++) {
+ Vector<String> vid_pid = entries[i].split("/");
+
+ if (vid_pid.size() < 2) {
+ continue;
+ }
+
+ print_verbose(vformat("Device Ignored -- Vendor: %s Product: %s", vid_pid[0], vid_pid[1]));
+ const uint16_t vid_unswapped = vid_pid[0].hex_to_int();
+ const uint16_t pid_unswapped = vid_pid[1].hex_to_int();
+ const uint16_t vid = BSWAP16(vid_unswapped);
+ const uint16_t pid = BSWAP16(pid_unswapped);
+
+ uint32_t full_id = (((uint32_t)vid) << 16) | ((uint16_t)pid);
+ ignored_device_ids.insert(full_id);
+ }
+ }
+
legacy_just_pressed_behavior = GLOBAL_DEF("input_devices/compatibility/legacy_just_pressed_behavior", false);
if (Engine::get_singleton()->is_editor_hint()) {
// Always use standard behavior in the editor.
diff --git a/core/input/input.h b/core/input/input.h
index 9cc596ee90..ec16871b72 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -154,6 +154,9 @@ private:
VelocityTrack mouse_velocity_track;
HashMap<int, VelocityTrack> touch_velocity_track;
HashMap<int, Joypad> joy_names;
+
+ HashSet<uint32_t> ignored_device_ids;
+
int fallback_mapping = -1;
CursorShape default_shape = CURSOR_ARROW;
@@ -328,6 +331,7 @@ public:
bool is_joy_known(int p_device);
String get_joy_guid(int p_device) const;
+ bool should_ignore_device(int p_vendor_id, int p_product_id) const;
void set_fallback_mapping(String p_guid);
void flush_buffered_events();
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 9bb987b670..a5fea09113 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -517,21 +517,31 @@ void Image::convert(Format p_new_format) {
return;
}
+ // Includes the main image.
+ const int mipmap_count = get_mipmap_count() + 1;
+
if (format > FORMAT_RGBE9995 || p_new_format > FORMAT_RGBE9995) {
ERR_FAIL_MSG("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead.");
} else if (format > FORMAT_RGBA8 || p_new_format > FORMAT_RGBA8) {
//use put/set pixel which is slower but works with non byte formats
- Image new_img(width, height, false, p_new_format);
+ Image new_img(width, height, mipmaps, p_new_format);
- for (int i = 0; i < width; i++) {
- for (int j = 0; j < height; j++) {
- new_img.set_pixel(i, j, get_pixel(i, j));
+ for (int mip = 0; mip < mipmap_count; mip++) {
+ Ref<Image> src_mip = get_image_from_mipmap(mip);
+ Ref<Image> new_mip = new_img.get_image_from_mipmap(mip);
+
+ for (int y = 0; y < src_mip->height; y++) {
+ for (int x = 0; x < src_mip->width; x++) {
+ new_mip->set_pixel(x, y, src_mip->get_pixel(x, y));
+ }
}
- }
- if (has_mipmaps()) {
- new_img.generate_mipmaps();
+ int mip_offset = 0;
+ int mip_size = 0;
+ new_img.get_mipmap_offset_and_size(mip, mip_offset, mip_size);
+
+ memcpy(new_img.data.ptrw() + mip_offset, new_mip->data.ptr(), mip_size);
}
_copy_internals_from(new_img);
@@ -539,113 +549,115 @@ void Image::convert(Format p_new_format) {
return;
}
- Image new_img(width, height, false, p_new_format);
-
- const uint8_t *rptr = data.ptr();
- uint8_t *wptr = new_img.data.ptrw();
+ Image new_img(width, height, mipmaps, p_new_format);
int conversion_type = format | p_new_format << 8;
- switch (conversion_type) {
- case FORMAT_L8 | (FORMAT_LA8 << 8):
- _convert<1, false, 1, true, true, true>(width, height, rptr, wptr);
- break;
- case FORMAT_L8 | (FORMAT_R8 << 8):
- _convert<1, false, 1, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_L8 | (FORMAT_RG8 << 8):
- _convert<1, false, 2, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_L8 | (FORMAT_RGB8 << 8):
- _convert<1, false, 3, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_L8 | (FORMAT_RGBA8 << 8):
- _convert<1, false, 3, true, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_LA8 | (FORMAT_L8 << 8):
- _convert<1, true, 1, false, true, true>(width, height, rptr, wptr);
- break;
- case FORMAT_LA8 | (FORMAT_R8 << 8):
- _convert<1, true, 1, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_LA8 | (FORMAT_RG8 << 8):
- _convert<1, true, 2, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_LA8 | (FORMAT_RGB8 << 8):
- _convert<1, true, 3, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_LA8 | (FORMAT_RGBA8 << 8):
- _convert<1, true, 3, true, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_R8 | (FORMAT_L8 << 8):
- _convert<1, false, 1, false, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_R8 | (FORMAT_LA8 << 8):
- _convert<1, false, 1, true, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_R8 | (FORMAT_RG8 << 8):
- _convert<1, false, 2, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_R8 | (FORMAT_RGB8 << 8):
- _convert<1, false, 3, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_R8 | (FORMAT_RGBA8 << 8):
- _convert<1, false, 3, true, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RG8 | (FORMAT_L8 << 8):
- _convert<2, false, 1, false, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RG8 | (FORMAT_LA8 << 8):
- _convert<2, false, 1, true, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RG8 | (FORMAT_R8 << 8):
- _convert<2, false, 1, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RG8 | (FORMAT_RGB8 << 8):
- _convert<2, false, 3, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RG8 | (FORMAT_RGBA8 << 8):
- _convert<2, false, 3, true, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGB8 | (FORMAT_L8 << 8):
- _convert<3, false, 1, false, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RGB8 | (FORMAT_LA8 << 8):
- _convert<3, false, 1, true, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RGB8 | (FORMAT_R8 << 8):
- _convert<3, false, 1, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGB8 | (FORMAT_RG8 << 8):
- _convert<3, false, 2, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGB8 | (FORMAT_RGBA8 << 8):
- _convert<3, false, 3, true, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGBA8 | (FORMAT_L8 << 8):
- _convert<3, true, 1, false, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RGBA8 | (FORMAT_LA8 << 8):
- _convert<3, true, 1, true, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RGBA8 | (FORMAT_R8 << 8):
- _convert<3, true, 1, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGBA8 | (FORMAT_RG8 << 8):
- _convert<3, true, 2, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGBA8 | (FORMAT_RGB8 << 8):
- _convert<3, true, 3, false, false, false>(width, height, rptr, wptr);
- break;
- }
-
- bool gen_mipmaps = mipmaps;
+ for (int mip = 0; mip < mipmap_count; mip++) {
+ int mip_offset = 0;
+ int mip_size = 0;
+ int mip_width = 0;
+ int mip_height = 0;
+ get_mipmap_offset_size_and_dimensions(mip, mip_offset, mip_size, mip_width, mip_height);
- _copy_internals_from(new_img);
+ const uint8_t *rptr = data.ptr() + mip_offset;
+ uint8_t *wptr = new_img.data.ptrw() + new_img.get_mipmap_offset(mip);
- if (gen_mipmaps) {
- generate_mipmaps();
+ switch (conversion_type) {
+ case FORMAT_L8 | (FORMAT_LA8 << 8):
+ _convert<1, false, 1, true, true, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_L8 | (FORMAT_R8 << 8):
+ _convert<1, false, 1, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_L8 | (FORMAT_RG8 << 8):
+ _convert<1, false, 2, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_L8 | (FORMAT_RGB8 << 8):
+ _convert<1, false, 3, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_L8 | (FORMAT_RGBA8 << 8):
+ _convert<1, false, 3, true, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_LA8 | (FORMAT_L8 << 8):
+ _convert<1, true, 1, false, true, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_LA8 | (FORMAT_R8 << 8):
+ _convert<1, true, 1, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_LA8 | (FORMAT_RG8 << 8):
+ _convert<1, true, 2, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_LA8 | (FORMAT_RGB8 << 8):
+ _convert<1, true, 3, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_LA8 | (FORMAT_RGBA8 << 8):
+ _convert<1, true, 3, true, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_R8 | (FORMAT_L8 << 8):
+ _convert<1, false, 1, false, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_R8 | (FORMAT_LA8 << 8):
+ _convert<1, false, 1, true, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_R8 | (FORMAT_RG8 << 8):
+ _convert<1, false, 2, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_R8 | (FORMAT_RGB8 << 8):
+ _convert<1, false, 3, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_R8 | (FORMAT_RGBA8 << 8):
+ _convert<1, false, 3, true, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RG8 | (FORMAT_L8 << 8):
+ _convert<2, false, 1, false, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RG8 | (FORMAT_LA8 << 8):
+ _convert<2, false, 1, true, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RG8 | (FORMAT_R8 << 8):
+ _convert<2, false, 1, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RG8 | (FORMAT_RGB8 << 8):
+ _convert<2, false, 3, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RG8 | (FORMAT_RGBA8 << 8):
+ _convert<2, false, 3, true, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGB8 | (FORMAT_L8 << 8):
+ _convert<3, false, 1, false, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGB8 | (FORMAT_LA8 << 8):
+ _convert<3, false, 1, true, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGB8 | (FORMAT_R8 << 8):
+ _convert<3, false, 1, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGB8 | (FORMAT_RG8 << 8):
+ _convert<3, false, 2, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGB8 | (FORMAT_RGBA8 << 8):
+ _convert<3, false, 3, true, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGBA8 | (FORMAT_L8 << 8):
+ _convert<3, true, 1, false, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGBA8 | (FORMAT_LA8 << 8):
+ _convert<3, true, 1, true, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGBA8 | (FORMAT_R8 << 8):
+ _convert<3, true, 1, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGBA8 | (FORMAT_RG8 << 8):
+ _convert<3, true, 2, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGBA8 | (FORMAT_RGB8 << 8):
+ _convert<3, true, 3, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ }
}
+
+ _copy_internals_from(new_img);
}
Image::Format Image::get_format() const {
@@ -3004,6 +3016,8 @@ ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
+ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_dds_mem_loader_func = nullptr;
void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr;
@@ -3475,6 +3489,10 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer);
ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer);
ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer);
+ ClassDB::bind_method(D_METHOD("load_dds_from_buffer", "buffer"), &Image::load_dds_from_buffer);
+
+ ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0));
+ ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0));
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data");
@@ -3825,6 +3843,36 @@ Error Image::load_bmp_from_buffer(const Vector<uint8_t> &p_array) {
return _load_from_buffer(p_array, _bmp_mem_loader_func);
}
+Error Image::load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale) {
+ ERR_FAIL_NULL_V_MSG(
+ _svg_scalable_mem_loader_func,
+ ERR_UNAVAILABLE,
+ "The SVG module isn't enabled. Recompile the Godot editor or export template binary with the `module_svg_enabled=yes` SCons option.");
+
+ int buffer_size = p_array.size();
+
+ ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER);
+
+ Ref<Image> image = _svg_scalable_mem_loader_func(p_array.ptr(), buffer_size, scale);
+ ERR_FAIL_COND_V(!image.is_valid(), ERR_PARSE_ERROR);
+
+ copy_internals_from(image);
+
+ return OK;
+}
+
+Error Image::load_svg_from_string(const String &p_svg_str, float scale) {
+ return load_svg_from_buffer(p_svg_str.to_utf8_buffer(), scale);
+}
+
+Error Image::load_dds_from_buffer(const Vector<uint8_t> &p_array) {
+ ERR_FAIL_NULL_V_MSG(
+ _dds_mem_loader_func,
+ ERR_UNAVAILABLE,
+ "The DDS module isn't enabled. Recompile the Godot editor or export template binary with the `module_dds_enabled=yes` SCons option.");
+ return _load_from_buffer(p_array, _dds_mem_loader_func);
+}
+
void Image::convert_rg_to_ra_rgba8() {
ERR_FAIL_COND(format != FORMAT_RGBA8);
ERR_FAIL_COND(!data.size());
diff --git a/core/io/image.h b/core/io/image.h
index 8e353a8bb7..f68543ba24 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -48,6 +48,7 @@ typedef Vector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
typedef Error (*SaveJPGFunc)(const String &p_path, const Ref<Image> &p_img, float p_quality);
typedef Vector<uint8_t> (*SaveJPGBufferFunc)(const Ref<Image> &p_img, float p_quality);
typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
+typedef Ref<Image> (*ScalableImageMemLoadFunc)(const uint8_t *p_data, int p_size, float p_scale);
typedef Error (*SaveWebPFunc)(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
typedef Vector<uint8_t> (*SaveWebPBufferFunc)(const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
@@ -148,6 +149,8 @@ public:
static ImageMemLoadFunc _webp_mem_loader_func;
static ImageMemLoadFunc _tga_mem_loader_func;
static ImageMemLoadFunc _bmp_mem_loader_func;
+ static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func;
+ static ImageMemLoadFunc _dds_mem_loader_func;
static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels);
static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels);
@@ -400,6 +403,10 @@ public:
Error load_webp_from_buffer(const Vector<uint8_t> &p_array);
Error load_tga_from_buffer(const Vector<uint8_t> &p_array);
Error load_bmp_from_buffer(const Vector<uint8_t> &p_array);
+ Error load_dds_from_buffer(const Vector<uint8_t> &p_array);
+
+ Error load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale = 1.0);
+ Error load_svg_from_string(const String &p_svg_str, float scale = 1.0);
void convert_rg_to_ra_rgba8();
void convert_ra_rgba8_to_rg();
diff --git a/core/io/json.cpp b/core/io/json.cpp
index a6e054a9fe..496400a5ea 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -299,9 +299,15 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
}
} break;
- default: {
+ case '"':
+ case '\\':
+ case '/': {
res = next;
} break;
+ default: {
+ r_err_str = "Invalid escape sequence.";
+ return ERR_PARSE_ERROR;
+ }
}
str += res;
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 2a7a675f2d..551d3268b8 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1775,9 +1775,9 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
case Variant::OBJECT: {
f->store_32(VARIANT_OBJECT);
Ref<Resource> res = p_property;
- if (res.is_null()) {
+ if (res.is_null() || res->get_meta(SNAME("_skip_save_"), false)) {
f->store_32(OBJECT_EMPTY);
- return; // don't save it
+ return; // Don't save it.
}
if (!res->is_built_in()) {
@@ -1942,7 +1942,7 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
case Variant::OBJECT: {
Ref<Resource> res = p_variant;
- if (res.is_null() || external_resources.has(res)) {
+ if (res.is_null() || external_resources.has(res) || res->get_meta(SNAME("_skip_save_"), false)) {
return;
}
@@ -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);
@@ -1968,14 +1970,17 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
if (E.usage & PROPERTY_USAGE_STORAGE) {
Variant value = res->get(E.name);
if (E.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
+ NonPersistentKey npk;
+ npk.base = res;
+ npk.property = E.name;
+ non_persistent_map[npk] = value;
+
Ref<Resource> sres = value;
if (sres.is_valid()) {
- NonPersistentKey npk;
- npk.base = res;
- npk.property = E.name;
- non_persistent_map[npk] = sres;
resource_set.insert(sres);
saved_resources.push_back(sres);
+ } else {
+ _find_resources(value);
}
} else {
_find_resources(value);
@@ -1983,7 +1988,6 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
}
}
- resource_set.insert(res);
saved_resources.push_back(res);
} break;
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index 30f1664983..e64485d404 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -139,7 +139,7 @@ class ResourceFormatSaverBinaryInstance {
bool operator<(const NonPersistentKey &p_key) const { return base == p_key.base ? property < p_key.property : base < p_key.base; }
};
- RBMap<NonPersistentKey, Ref<Resource>> non_persistent_map;
+ RBMap<NonPersistentKey, Variant> non_persistent_map;
HashMap<StringName, int> string_map;
Vector<StringName> strings;
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index dc1de6b9ce..fcf4a727ca 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -394,6 +394,15 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_name(const String
return Ref<ResourceImporter>();
}
+void ResourceFormatImporter::add_importer(const Ref<ResourceImporter> &p_importer, bool p_first_priority) {
+ ERR_FAIL_COND(p_importer.is_null());
+ if (p_first_priority) {
+ importers.insert(0, p_importer);
+ } else {
+ importers.push_back(p_importer);
+ }
+}
+
void ResourceFormatImporter::get_importers_for_extension(const String &p_extension, List<Ref<ResourceImporter>> *r_importers) {
for (int i = 0; i < importers.size(); i++) {
List<String> local_exts;
@@ -472,20 +481,13 @@ ResourceFormatImporter::ResourceFormatImporter() {
singleton = this;
}
+//////////////
+
void ResourceImporter::_bind_methods() {
BIND_ENUM_CONSTANT(IMPORT_ORDER_DEFAULT);
BIND_ENUM_CONSTANT(IMPORT_ORDER_SCENE);
}
-void ResourceFormatImporter::add_importer(const Ref<ResourceImporter> &p_importer, bool p_first_priority) {
- ERR_FAIL_COND(p_importer.is_null());
- if (p_first_priority) {
- importers.insert(0, p_importer);
- } else {
- importers.push_back(p_importer);
- }
-}
-
/////
Error ResourceFormatImporterSaver::set_uid(const String &p_path, ResourceUID::ID p_uid) {
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 1fe662b1fa..df0253349c 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -275,10 +275,10 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
#ifdef TOOLS_ENABLED
Ref<FileAccess> file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES);
- ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref<Resource>(), "Resource file not found: " + p_path + ".");
+ ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), Ref<Resource>(), vformat("Resource file not found: %s (expected type: %s)", p_path, p_type_hint));
#endif
- ERR_FAIL_V_MSG(Ref<Resource>(), "No loader found for resource: " + p_path + ".");
+ ERR_FAIL_V_MSG(Ref<Resource>(), vformat("No loader found for resource: %s (expected type: %s)", p_path, p_type_hint));
}
void ResourceLoader::_thread_load_function(void *p_userdata) {
diff --git a/core/object/callable_method_pointer.cpp b/core/object/callable_method_pointer.cpp
index b53985e6b7..ed400788b1 100644
--- a/core/object/callable_method_pointer.cpp
+++ b/core/object/callable_method_pointer.cpp
@@ -38,13 +38,10 @@ bool CallableCustomMethodPointerBase::compare_equal(const CallableCustom *p_a, c
return false;
}
- for (uint32_t i = 0; i < a->comp_size; i++) {
- if (a->comp_ptr[i] != b->comp_ptr[i]) {
- return false;
- }
- }
-
- return true;
+ // Avoid sorting by memory address proximity, which leads to unpredictable performance over time
+ // due to the reuse of old addresses for newer objects. Use byte-wise comparison to leverage the
+ // backwards encoding of little-endian systems as a way to decouple spatiality and time.
+ return memcmp(a->comp_ptr, b->comp_ptr, a->comp_size * 4) == 0;
}
bool CallableCustomMethodPointerBase::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -55,15 +52,8 @@ bool CallableCustomMethodPointerBase::compare_less(const CallableCustom *p_a, co
return a->comp_size < b->comp_size;
}
- for (uint32_t i = 0; i < a->comp_size; i++) {
- if (a->comp_ptr[i] == b->comp_ptr[i]) {
- continue;
- }
-
- return a->comp_ptr[i] < b->comp_ptr[i];
- }
-
- return false;
+ // See note in compare_equal().
+ return memcmp(a->comp_ptr, b->comp_ptr, a->comp_size * 4) < 0;
}
CallableCustom::CompareEqualFunc CallableCustomMethodPointerBase::get_compare_equal_func() const {
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index cc4a29164d..c8c50fb957 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -53,7 +53,7 @@ MethodDefinition D_METHODP(const char *p_name, const char *const **p_args, uint3
#endif
ClassDB::APIType ClassDB::current_api = API_CORE;
-HashMap<ClassDB::APIType, uint64_t> ClassDB::api_hashes_cache;
+HashMap<ClassDB::APIType, uint32_t> ClassDB::api_hashes_cache;
void ClassDB::set_current_api(APIType p_api) {
DEV_ASSERT(!api_hashes_cache.has(p_api)); // This API type may not be suitable for caching of hash if it can change later.
@@ -163,7 +163,7 @@ ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) {
return ti->api;
}
-uint64_t ClassDB::get_api_hash(APIType p_api) {
+uint32_t ClassDB::get_api_hash(APIType p_api) {
OBJTYPE_RLOCK;
#ifdef DEBUG_METHODS_ENABLED
diff --git a/core/object/class_db.h b/core/object/class_db.h
index ce64336a45..3aae3b452e 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -155,7 +155,7 @@ public:
#endif
static APIType current_api;
- static HashMap<APIType, uint64_t> api_hashes_cache;
+ static HashMap<APIType, uint32_t> api_hashes_cache;
static void _add_class2(const StringName &p_class, const StringName &p_inherits);
@@ -246,7 +246,7 @@ public:
static APIType get_api_type(const StringName &p_class);
- static uint64_t get_api_hash(APIType p_api);
+ static uint32_t get_api_hash(APIType p_api);
template <typename>
struct member_function_traits;
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.cpp b/core/object/object.cpp
index 4d19a2c75b..4ae0ecdefd 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -486,19 +486,21 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
const ObjectGDExtension *current_extension = _extension;
while (current_extension) {
p_list->push_back(PropertyInfo(Variant::NIL, current_extension->class_name, PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
+
ClassDB::get_property_list(current_extension->class_name, p_list, true, this);
- current_extension = current_extension->parent;
- }
- }
- if (_extension && _extension->get_property_list) {
- uint32_t pcount;
- const GDExtensionPropertyInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount);
- for (uint32_t i = 0; i < pcount; i++) {
- p_list->push_back(PropertyInfo(pinfo[i]));
- }
- if (_extension->free_property_list) {
- _extension->free_property_list(_extension_instance, pinfo);
+ if (current_extension->get_property_list) {
+ uint32_t pcount;
+ const GDExtensionPropertyInfo *pinfo = current_extension->get_property_list(_extension_instance, &pcount);
+ for (uint32_t i = 0; i < pcount; i++) {
+ p_list->push_back(PropertyInfo(pinfo[i]));
+ }
+ if (current_extension->free_property_list) {
+ current_extension->free_property_list(_extension_instance, pinfo);
+ }
+ }
+
+ current_extension = current_extension->parent;
}
}
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/script_language.h b/core/object/script_language.h
index 2b685c77a3..3ea6a6e4c3 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -263,6 +263,7 @@ public:
};
struct ScriptError {
+ String path;
int line = -1;
int column = -1;
String message;
diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp
index b908b64d73..5e21490164 100644
--- a/core/os/main_loop.cpp
+++ b/core/os/main_loop.cpp
@@ -52,15 +52,7 @@ void MainLoop::_bind_methods() {
GDVIRTUAL_BIND(_finalize);
}
-void MainLoop::set_initialize_script(const Ref<Script> &p_initialize_script) {
- initialize_script = p_initialize_script;
-}
-
void MainLoop::initialize() {
- if (initialize_script.is_valid()) {
- set_script(initialize_script);
- }
-
GDVIRTUAL_CALL(_initialize);
}
diff --git a/core/os/main_loop.h b/core/os/main_loop.h
index 36eeb35aad..90cad009b1 100644
--- a/core/os/main_loop.h
+++ b/core/os/main_loop.h
@@ -39,8 +39,6 @@
class MainLoop : public Object {
GDCLASS(MainLoop, Object);
- Ref<Script> initialize_script;
-
protected:
static void _bind_methods();
@@ -69,8 +67,6 @@ public:
virtual bool process(double p_time);
virtual void finalize();
- void set_initialize_script(const Ref<Script> &p_initialize_script);
-
MainLoop() {}
virtual ~MainLoop() {}
};
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/locales.h b/core/string/locales.h
index 8a7efb4fd1..840fca65a7 100644
--- a/core/string/locales.h
+++ b/core/string/locales.h
@@ -1057,8 +1057,8 @@ static const char *script_list[][2] = {
{ "Hangul", "Hang" },
{ "Han", "Hani" },
{ "Hanunoo", "Hano" },
- { "Simplified", "Hans" },
- { "Traditional", "Hant" },
+ { "Simplified Han", "Hans" },
+ { "Traditional Han", "Hant" },
{ "Hatran", "Hatr" },
{ "Hebrew", "Hebr" },
{ "Hiragana", "Hira" },
@@ -1110,7 +1110,7 @@ static const char *script_list[][2] = {
{ "Mro", "Mroo" },
{ "Meitei Mayek", "Mtei" },
{ "Multani", "Mult" },
- { "Myanmar (Burmese)", "Mymr" },
+ { "Myanmar / Burmese", "Mymr" },
{ "​Nag Mundari", "Nagm" },
{ "Nandinagari", "Nand" },
{ "Old North Arabian", "Narb" },
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/string/translation.cpp b/core/string/translation.cpp
index 3ca2e5ccdf..02380c92bb 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -82,6 +82,15 @@ void Translation::_set_messages(const Dictionary &p_messages) {
void Translation::set_locale(const String &p_locale) {
locale = TranslationServer::get_singleton()->standardize_locale(p_locale);
+ if (Thread::is_main_thread()) {
+ _notify_translation_changed_if_applies();
+ } else {
+ // Avoid calling non-thread-safe functions here.
+ callable_mp(this, &Translation::_notify_translation_changed_if_applies).call_deferred();
+ }
+}
+
+void Translation::_notify_translation_changed_if_applies() {
if (OS::get_singleton()->get_main_loop() && TranslationServer::get_singleton()->get_loaded_locales().has(get_locale())) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
diff --git a/core/string/translation.h b/core/string/translation.h
index 01d239f81c..ca8b460312 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -47,6 +47,8 @@ class Translation : public Resource {
virtual Dictionary _get_messages() const;
virtual void _set_messages(const Dictionary &p_messages);
+ void _notify_translation_changed_if_applies();
+
protected:
static void _bind_methods();
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/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 9d597d8e23..3b7974f523 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -362,8 +362,8 @@
<return type="float" />
<param index="0" name="x" type="float" />
<description>
- The natural exponential function. It raises the mathematical constant [b]e[/b] to the power of [param x] and returns it.
- [b]e[/b] has an approximate value of 2.71828, and can be obtained with [code]exp(1)[/code].
+ The natural exponential function. It raises the mathematical constant [i]e[/i] to the power of [param x] and returns it.
+ [i]e[/i] has an approximate value of 2.71828, and can be obtained with [code]exp(1)[/code].
For exponents to other bases use the method [method pow].
[codeblock]
var a = exp(2) # Approximately 7.39
diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml
index ec55ccedb2..5d54af60ec 100644
--- a/doc/classes/AStarGrid2D.xml
+++ b/doc/classes/AStarGrid2D.xml
@@ -17,7 +17,7 @@
[/gdscript]
[csharp]
AStarGrid2D astarGrid = new AStarGrid2D();
- astarGrid.Size = new Vector2I(32, 32);
+ astarGrid.Region = new Rect2I(0, 0, 32, 32);
astarGrid.CellSize = new Vector2I(16, 16);
astarGrid.Update();
GD.Print(astarGrid.GetIdPath(Vector2I.Zero, new Vector2I(3, 4))); // prints (0, 0), (1, 1), (2, 2), (3, 3), (3, 4)
diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml
index 89b3f843af..4f89e9b015 100644
--- a/doc/classes/Area3D.xml
+++ b/doc/classes/Area3D.xml
@@ -6,7 +6,7 @@
<description>
[Area3D] is a region of 3D space defined by one or multiple [CollisionShape3D] or [CollisionPolygon3D] child nodes. It detects when other [CollisionObject3D]s enter or exit it, and it also keeps track of which collision objects haven't exited it yet (i.e. which one are overlapping it).
This node can also locally alter or override physics parameters (gravity, damping) and route audio to custom audio buses.
- [b]Warning:[/b] Using a [ConcavePolygonShape3D] inside a [CollisionShape3D] child of this node (created e.g. by using the [i]Create Trimesh Collision Sibling[/i] option in the [i]Mesh[/i] menu that appears when selecting a [MeshInstance3D] node) may give unexpected results, since this collision shape is hollow. If this is not desired, it has to be split into multiple [ConvexPolygonShape3D]s or primitive shapes like [BoxShape3D], or in some cases it may be replaceable by a [CollisionPolygon3D].
+ [b]Warning:[/b] Using a [ConcavePolygonShape3D] inside a [CollisionShape3D] child of this node (created e.g. by using the [b]Create Trimesh Collision Sibling[/b] option in the [b]Mesh[/b] menu that appears when selecting a [MeshInstance3D] node) may give unexpected results, since this collision shape is hollow. If this is not desired, it has to be split into multiple [ConvexPolygonShape3D]s or primitive shapes like [BoxShape3D], or in some cases it may be replaceable by a [CollisionPolygon3D].
</description>
<tutorials>
<link title="Using Area2D">$DOCS_URL/tutorials/physics/using_area_2d.html</link>
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/AudioEffectDelay.xml b/doc/classes/AudioEffectDelay.xml
index fa255e3201..eae49f6a98 100644
--- a/doc/classes/AudioEffectDelay.xml
+++ b/doc/classes/AudioEffectDelay.xml
@@ -21,34 +21,34 @@
Feedback delay time in milliseconds.
</member>
<member name="feedback_level_db" type="float" setter="set_feedback_level_db" getter="get_feedback_level_db" default="-6.0">
- Sound level for [code]tap1[/code].
+ Sound level for feedback.
</member>
<member name="feedback_lowpass" type="float" setter="set_feedback_lowpass" getter="get_feedback_lowpass" default="16000.0">
Low-pass filter for feedback, in Hz. Frequencies below this value are filtered out of the source signal.
</member>
<member name="tap1_active" type="bool" setter="set_tap1_active" getter="is_tap1_active" default="true">
- If [code]true[/code], [code]tap1[/code] will be enabled.
+ If [code]true[/code], the first tap will be enabled.
</member>
<member name="tap1_delay_ms" type="float" setter="set_tap1_delay_ms" getter="get_tap1_delay_ms" default="250.0">
- [code]tap1[/code] delay time in milliseconds.
+ First tap delay time in milliseconds.
</member>
<member name="tap1_level_db" type="float" setter="set_tap1_level_db" getter="get_tap1_level_db" default="-6.0">
- Sound level for [code]tap1[/code].
+ Sound level for the first tap.
</member>
<member name="tap1_pan" type="float" setter="set_tap1_pan" getter="get_tap1_pan" default="0.2">
- Pan position for [code]tap1[/code]. Value can range from -1 (fully left) to 1 (fully right).
+ Pan position for the first tap. Value can range from -1 (fully left) to 1 (fully right).
</member>
<member name="tap2_active" type="bool" setter="set_tap2_active" getter="is_tap2_active" default="true">
- If [code]true[/code], [code]tap2[/code] will be enabled.
+ If [code]true[/code], the second tap will be enabled.
</member>
<member name="tap2_delay_ms" type="float" setter="set_tap2_delay_ms" getter="get_tap2_delay_ms" default="500.0">
- [b]Tap2[/b] delay time in milliseconds.
+ Second tap delay time in milliseconds.
</member>
<member name="tap2_level_db" type="float" setter="set_tap2_level_db" getter="get_tap2_level_db" default="-12.0">
- Sound level for [code]tap2[/code].
+ Sound level for the second tap.
</member>
<member name="tap2_pan" type="float" setter="set_tap2_pan" getter="get_tap2_pan" default="-0.4">
- Pan position for [code]tap2[/code]. Value can range from -1 (fully left) to 1 (fully right).
+ Pan position for the second tap. Value can range from -1 (fully left) to 1 (fully right).
</member>
</members>
</class>
diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml
index 651d2a4b49..f4c9adcaec 100644
--- a/doc/classes/AudioStreamPlayer3D.xml
+++ b/doc/classes/AudioStreamPlayer3D.xml
@@ -85,7 +85,7 @@
Attenuation factor used if listener is outside of [member emission_angle_degrees] and [member emission_angle_enabled] is set, in decibels.
</member>
<member name="max_db" type="float" setter="set_max_db" getter="get_max_db" default="3.0">
- Sets the absolute maximum of the soundlevel, in decibels.
+ Sets the absolute maximum of the sound level, in decibels.
</member>
<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="0.0">
The distance past which the sound can no longer be heard at all. Only has an effect if set to a value greater than [code]0.0[/code]. [member max_distance] works in tandem with [member unit_size]. However, unlike [member unit_size] whose behavior depends on the [member attenuation_model], [member max_distance] always works in a linear fashion. This can be used to prevent the [AudioStreamPlayer3D] from requiring audio mixing when the listener is far away, which saves CPU resources.
diff --git a/doc/classes/BackBufferCopy.xml b/doc/classes/BackBufferCopy.xml
index a671b85c83..e4e6f1d76d 100644
--- a/doc/classes/BackBufferCopy.xml
+++ b/doc/classes/BackBufferCopy.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="BackBufferCopy" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Copies a region of the screen (or the whole screen) to a buffer so it can be accessed in your shader scripts using the screen texture (i.e. a uniform sampler with ``hint_screen_texture``).
+ Copies a region of the screen (or the whole screen) to a buffer so it can be accessed in your shader scripts using the screen texture (i.e. a uniform sampler with [code]hint_screen_texture[/code]).
</brief_description>
<description>
Node for back-buffering the currently-displayed screen. The region defined in the [BackBufferCopy] node is buffered with the content of the screen it covers, or the entire screen according to the copy mode set. Use the screen texture in your shader scripts to access the buffer.
diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml
index 86cc86c752..949dcc24d0 100644
--- a/doc/classes/BaseMaterial3D.xml
+++ b/doc/classes/BaseMaterial3D.xml
@@ -269,7 +269,7 @@
Specifies the channel of the [member metallic_texture] in which the metallic information is stored. This is useful when you store the information for multiple effects in a single texture. For example if you stored metallic in the red channel, roughness in the blue, and ambient occlusion in the green you could reduce the number of textures you use.
</member>
<member name="msdf_outline_size" type="float" setter="set_msdf_outline_size" getter="get_msdf_outline_size" default="0.0">
- The width of the shape outine.
+ The width of the shape outline.
</member>
<member name="msdf_pixel_range" type="float" setter="set_msdf_pixel_range" getter="get_msdf_pixel_range" default="4.0">
The width of the range around the shape between the minimum and maximum representable signed distance.
diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml
index 74523ee397..eaacdd590d 100644
--- a/doc/classes/CPUParticles2D.xml
+++ b/doc/classes/CPUParticles2D.xml
@@ -169,7 +169,7 @@
The sphere's radius if [member emission_shape] is set to [constant EMISSION_SHAPE_SPHERE].
</member>
<member name="emitting" type="bool" setter="set_emitting" getter="is_emitting" default="true">
- If [code]true[/code], particles are being emitted.
+ If [code]true[/code], particles are being emitted. [member emitting] can be used to start and stop particles from emitting. However, if [member one_shot] is [code]true[/code] setting [member emitting] to [code]true[/code] will not restart the emission cycle until after all active particles finish processing. You can use the [signal finished] signal to be notified once all active particles finish processing.
</member>
<member name="explosiveness" type="float" setter="set_explosiveness_ratio" getter="get_explosiveness_ratio" default="0.0">
How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins.
@@ -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..f03c463049 100644
--- a/doc/classes/CPUParticles3D.xml
+++ b/doc/classes/CPUParticles3D.xml
@@ -183,7 +183,7 @@
The sphere's radius if [enum EmissionShape] is set to [constant EMISSION_SHAPE_SPHERE].
</member>
<member name="emitting" type="bool" setter="set_emitting" getter="is_emitting" default="true">
- If [code]true[/code], particles are being emitted.
+ If [code]true[/code], particles are being emitted. [member emitting] can be used to start and stop particles from emitting. However, if [member one_shot] is [code]true[/code] setting [member emitting] to [code]true[/code] will not restart the emission cycle until after all active particles finish processing. You can use the [signal finished] signal to be notified once all active particles finish processing.
</member>
<member name="explosiveness" type="float" setter="set_explosiveness_ratio" getter="get_explosiveness_ratio" default="0.0">
How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins.
@@ -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/CodeEdit.xml b/doc/classes/CodeEdit.xml
index ab63d66aba..0e829127f2 100644
--- a/doc/classes/CodeEdit.xml
+++ b/doc/classes/CodeEdit.xml
@@ -248,12 +248,20 @@
Returns the full text with char [code]0xFFFF[/code] at the caret location.
</description>
</method>
- <method name="get_text_for_symbol_lookup">
+ <method name="get_text_for_symbol_lookup" qualifiers="const">
<return type="String" />
<description>
Returns the full text with char [code]0xFFFF[/code] at the cursor location.
</description>
</method>
+ <method name="get_text_with_cursor_char" qualifiers="const">
+ <return type="String" />
+ <param index="0" name="line" type="int" />
+ <param index="1" name="column" type="int" />
+ <description>
+ Returns the full text with char [code]0xFFFF[/code] at the specified location.
+ </description>
+ </method>
<method name="has_auto_brace_completion_close_key" qualifiers="const">
<return type="bool" />
<param index="0" name="close_key" type="String" />
@@ -435,7 +443,7 @@
<return type="void" />
<param index="0" name="force" type="bool" />
<description>
- Submits all completion options added with [method add_code_completion_option]. Will try to force the autoccomplete menu to popup, if [param force] is [code]true[/code].
+ Submits all completion options added with [method add_code_completion_option]. Will try to force the autocomplete menu to popup, if [param force] is [code]true[/code].
[b]Note:[/b] This will replace all current candidates.
</description>
</method>
diff --git a/doc/classes/CollisionPolygon2D.xml b/doc/classes/CollisionPolygon2D.xml
index 783c856066..d6f3b7cb5d 100644
--- a/doc/classes/CollisionPolygon2D.xml
+++ b/doc/classes/CollisionPolygon2D.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
A node that provides a thickened polygon shape (a prism) to a [CollisionObject2D] parent and allows to edit it. The polygon can be concave or convex. This can give a detection shape to an [Area2D] or turn [PhysicsBody2D] into a solid object.
- [b]Warning:[/b] A non-uniformly scaled [CollisionShape3D] will likely not behave as expected. Make sure to keep its scale the same on all axes and adjust its shape resource instead.
+ [b]Warning:[/b] A non-uniformly scaled [CollisionShape2D] will likely not behave as expected. Make sure to keep its scale the same on all axes and adjust its shape resource instead.
</description>
<tutorials>
</tutorials>
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/Control.xml b/doc/classes/Control.xml
index 9544dce19a..ee790b6968 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -1094,15 +1094,15 @@
</signal>
<signal name="mouse_entered">
<description>
- Emitted when the mouse enters the control's [code]Rect[/code] area, provided its [member mouse_filter] lets the event reach it.
- [b]Note:[/b] [signal mouse_entered] will not be emitted if the mouse enters a child [Control] node before entering the parent's [code]Rect[/code] area, at least until the mouse is moved to reach the parent's [code]Rect[/code] area.
+ Emitted when the mouse cursor enters the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not.
+ [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the signal.
</description>
</signal>
<signal name="mouse_exited">
<description>
- Emitted when the mouse leaves the control's [code]Rect[/code] area, provided its [member mouse_filter] lets the event reach it.
- [b]Note:[/b] [signal mouse_exited] will be emitted if the mouse enters a child [Control] node, even if the mouse cursor is still inside the parent's [code]Rect[/code] area.
- If you want to check whether the mouse truly left the area, ignoring any top nodes, you can use code like this:
+ Emitted when the mouse cursor leaves the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not.
+ [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the signal.
+ [b]Note:[/b] If you want to check whether the mouse truly left the area, ignoring any top nodes, you can use code like this:
[codeblock]
func _on_mouse_exited():
if not Rect2(Vector2(), size).has_point(get_local_mouse_position()):
@@ -1140,10 +1140,12 @@
Sent when the node changes size. Use [member size] to get the new size.
</constant>
<constant name="NOTIFICATION_MOUSE_ENTER" value="41">
- Sent when the mouse pointer enters the node.
+ Sent when the mouse cursor enters the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not.
+ [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the notification.
</constant>
<constant name="NOTIFICATION_MOUSE_EXIT" value="42">
- Sent when the mouse pointer exits the node.
+ Sent when the mouse cursor leaves the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not.
+ [b]Note:[/b] [member CanvasItem.z_index] doesn't affect, which Control receives the notification.
</constant>
<constant name="NOTIFICATION_FOCUS_ENTER" value="43">
Sent when the node grabs focus.
@@ -1299,13 +1301,13 @@
Tells the parent [Container] to align the node with its end, either the bottom or the right edge. It is mutually exclusive with [constant SIZE_FILL] and other shrink size flags, but can be used with [constant SIZE_EXPAND] in some containers. Use with [member size_flags_horizontal] and [member size_flags_vertical].
</constant>
<constant name="MOUSE_FILTER_STOP" value="0" enum="MouseFilter">
- The control will receive mouse button input events through [method _gui_input] if clicked on. And the control will receive the [signal mouse_entered] and [signal mouse_exited] signals. These events are automatically marked as handled, and they will not propagate further to other controls. This also results in blocking signals in other controls.
+ The control will receive mouse movement input events and mouse button input events if clicked on through [method _gui_input]. And the control will receive the [signal mouse_entered] and [signal mouse_exited] signals. These events are automatically marked as handled, and they will not propagate further to other controls. This also results in blocking signals in other controls.
</constant>
<constant name="MOUSE_FILTER_PASS" value="1" enum="MouseFilter">
- The control will receive mouse button input events through [method _gui_input] if clicked on. And the control will receive the [signal mouse_entered] and [signal mouse_exited] signals. If this control does not handle the event, the parent control (if any) will be considered, and so on until there is no more parent control to potentially handle it. This also allows signals to fire in other controls. If no control handled it, the event will be passed to [method Node._unhandled_input] for further processing.
+ The control will receive mouse movement input events and mouse button input events if clicked on through [method _gui_input]. And the control will receive the [signal mouse_entered] and [signal mouse_exited] signals. If this control does not handle the event, the parent control (if any) will be considered, and so on until there is no more parent control to potentially handle it. This also allows signals to fire in other controls. If no control handled it, the event will be passed to [method Node._shortcut_input] for further processing.
</constant>
<constant name="MOUSE_FILTER_IGNORE" value="2" enum="MouseFilter">
- The control will not receive mouse button input events through [method _gui_input]. The control will also not receive the [signal mouse_entered] nor [signal mouse_exited] signals. This will not block other controls from receiving these events or firing the signals. Ignored events will not be handled automatically.
+ The control will not receive mouse movement input events and mouse button input events if clicked on through [method _gui_input]. The control will also not receive the [signal mouse_entered] nor [signal mouse_exited] signals. This will not block other controls from receiving these events or firing the signals. Ignored events will not be handled automatically.
</constant>
<constant name="GROW_DIRECTION_BEGIN" value="0" enum="GrowDirection">
The control will grow to the left or top to make up if its minimum size is changed to be greater than its current size on the respective axis.
diff --git a/doc/classes/ConvexPolygonShape2D.xml b/doc/classes/ConvexPolygonShape2D.xml
index 13134540e0..4f86f3830b 100644
--- a/doc/classes/ConvexPolygonShape2D.xml
+++ b/doc/classes/ConvexPolygonShape2D.xml
@@ -6,7 +6,7 @@
<description>
A 2D convex polygon shape, intended for use in physics. Used internally in [CollisionPolygon2D] when it's in [code]BUILD_SOLIDS[/code] mode.
[ConvexPolygonShape2D] is [i]solid[/i], which means it detects collisions from objects that are fully inside it, unlike [ConcavePolygonShape2D] which is hollow. This makes it more suitable for both detection and physics.
- [b]Convex decomposition:[/b] A concave polygon can be split up into several convex polygons. This allows dynamic physics bodies to have complex concave collisions (at a performance cost) and can be achieved by using several [ConvexPolygonShape3D] nodes or by using the [CollisionPolygon2D] node in [code]BUILD_SOLIDS[/code] mode. To generate a collision polygon from a sprite, select the [Sprite2D] node, go to the [b]Sprite2D[/b] menu that appears above the viewport, and choose [b]Create Polygon2D Sibling[/b].
+ [b]Convex decomposition:[/b] A concave polygon can be split up into several convex polygons. This allows dynamic physics bodies to have complex concave collisions (at a performance cost) and can be achieved by using several [ConvexPolygonShape2D] nodes or by using the [CollisionPolygon2D] node in [code]BUILD_SOLIDS[/code] mode. To generate a collision polygon from a sprite, select the [Sprite2D] node, go to the [b]Sprite2D[/b] menu that appears above the viewport, and choose [b]Create Polygon2D Sibling[/b].
[b]Performance:[/b] [ConvexPolygonShape2D] is faster to check collisions against compared to [ConcavePolygonShape2D], but it is slower than primitive collision shapes such as [CircleShape2D] and [RectangleShape2D]. Its use should generally be limited to medium-sized objects that cannot have their collision accurately represented by primitive shapes.
</description>
<tutorials>
diff --git a/doc/classes/CylinderShape3D.xml b/doc/classes/CylinderShape3D.xml
index 3d1204baf0..8bec199ab6 100644
--- a/doc/classes/CylinderShape3D.xml
+++ b/doc/classes/CylinderShape3D.xml
@@ -4,9 +4,9 @@
A 3D cylinder shape used for physics collision.
</brief_description>
<description>
- A 2D capsule shape, intended for use in physics. Usually used to provide a shape for a [CollisionShape3D].
+ A 3D cylinder shape, intended for use in physics. Usually used to provide a shape for a [CollisionShape3D].
[b]Note:[/b] There are several known bugs with cylinder collision shapes. Using [CapsuleShape3D] or [BoxShape3D] instead is recommended.
- [b]Performance:[/b] [CylinderShape3D] is fast to check collisions against, but it is slower than [CapsuleShape3D], [BoxShape3D], and [CylinderShape3D].
+ [b]Performance:[/b] [CylinderShape3D] is fast to check collisions against, but it is slower than [CapsuleShape3D], [BoxShape3D], and [SphereShape3D].
</description>
<tutorials>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml
index 3f165c2ac5..b39c5c9699 100644
--- a/doc/classes/Dictionary.xml
+++ b/doc/classes/Dictionary.xml
@@ -261,7 +261,7 @@
[/csharp]
[/codeblocks]
[b]Note:[/b] Dictionaries with the same entries but in a different order will not have the same hash.
- [b]Note:[/b] Dictionaries with equal hash values are [i]not[/i] guaranteed to be the same, because of hash collisions. On the countrary, dictionaries with different hash values are guaranteed to be different.
+ [b]Note:[/b] Dictionaries with equal hash values are [i]not[/i] guaranteed to be the same, because of hash collisions. On the contrary, dictionaries with different hash values are guaranteed to be different.
</description>
</method>
<method name="is_empty" qualifiers="const">
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index b74dbdce54..465855cc92 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -16,6 +16,12 @@
Returns the user's clipboard as a string if possible.
</description>
</method>
+ <method name="clipboard_get_image" qualifiers="const">
+ <return type="Image" />
+ <description>
+ Returns the user's clipboard as an image if possible.
+ </description>
+ </method>
<method name="clipboard_get_primary" qualifiers="const">
<return type="String" />
<description>
@@ -26,7 +32,13 @@
<method name="clipboard_has" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if there is content on the user's clipboard.
+ Returns [code]true[/code] if there is a text content on the user's clipboard.
+ </description>
+ </method>
+ <method name="clipboard_has_image" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if there is an image content on the user's clipboard.
</description>
</method>
<method name="clipboard_set">
@@ -96,6 +108,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>
@@ -757,7 +787,7 @@
<method name="is_touchscreen_available" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if touch events are available (Android or iOS), the capability is detected on the Webplatform or if [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is [code]true[/code].
+ Returns [code]true[/code] if touch events are available (Android or iOS), the capability is detected on the Web platform or if [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is [code]true[/code].
</description>
</method>
<method name="keyboard_get_current_layout" qualifiers="const">
@@ -775,6 +805,14 @@
[b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows.
</description>
</method>
+ <method name="keyboard_get_label_from_physical" qualifiers="const">
+ <return type="int" enum="Key" />
+ <param index="0" name="keycode" type="int" enum="Key" />
+ <description>
+ Converts a physical (US QWERTY) [param keycode] to localized label printed on the key in the active keyboard layout.
+ [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows.
+ </description>
+ </method>
<method name="keyboard_get_layout_count" qualifiers="const">
<return type="int" />
<description>
@@ -1721,6 +1759,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/EditorDebuggerPlugin.xml b/doc/classes/EditorDebuggerPlugin.xml
index 3910566c06..6f6b4f69e4 100644
--- a/doc/classes/EditorDebuggerPlugin.xml
+++ b/doc/classes/EditorDebuggerPlugin.xml
@@ -81,7 +81,7 @@
<return type="Array" />
<description>
Returns an array of [EditorDebuggerSession] currently available to this debugger plugin.
- Note: Not sessions in the array may be inactive, check their state via [method EditorDebuggerSession.is_active]
+ [b]Note:[/b] Not sessions in the array may be inactive, check their state via [method EditorDebuggerSession.is_active]
</description>
</method>
</methods>
diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml
index 9c185a2ddb..5f9e1e3684 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 [b]must[/b] 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,13 +192,21 @@
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" />
<param index="1" name="file" type="PackedByteArray" />
<param index="2" name="remap" type="bool" />
<description>
- Adds a custom file to be exported. [param path] is the virtual path that can be used to load the file, [param file] is the binary data of the file. If [param remap] is [code]true[/code], file will not be exported, but instead remapped to the given [param path].
+ Adds a custom file to be exported. [param path] is the virtual path that can be used to load the file, [param file] is the binary data of the file.
+ When called inside [method _export_file] and [param remap] is [code]true[/code], the current file will not be exported, but instead remapped to this custom file. [param remap] is ignored when called in other places.
</description>
</method>
<method name="add_ios_bundle_file">
diff --git a/doc/classes/EditorFeatureProfile.xml b/doc/classes/EditorFeatureProfile.xml
index d652759dc7..3aa1e63aac 100644
--- a/doc/classes/EditorFeatureProfile.xml
+++ b/doc/classes/EditorFeatureProfile.xml
@@ -51,6 +51,7 @@
<param index="0" name="path" type="String" />
<description>
Loads an editor feature profile from a file. The file must follow the JSON format obtained by using the feature profile manager's [b]Export[/b] button or the [method save_to_file] method.
+ [b]Note:[/b] Feature profiles created via the user interface are loaded from the [code]feature_profiles[/code] directory, as a file with the [code].profile[/code] extension. The editor configuration folder can be found by using [method EditorPaths.get_config_dir].
</description>
</method>
<method name="save_to_file">
@@ -58,6 +59,7 @@
<param index="0" name="path" type="String" />
<description>
Saves the editor feature profile to a file in JSON format. It can then be imported using the feature profile manager's [b]Import[/b] button or the [method load_from_file] method.
+ [b]Note:[/b] Feature profiles created via the user interface are saved in the [code]feature_profiles[/code] directory, as a file with the [code].profile[/code] extension. The editor configuration folder can be found by using [method EditorPaths.get_config_dir].
</description>
</method>
<method name="set_disable_class">
diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml
index f8002c260f..cabd9c0da6 100644
--- a/doc/classes/EditorInterface.xml
+++ b/doc/classes/EditorInterface.xml
@@ -54,6 +54,14 @@
Returns the current directory being viewed in the [FileSystemDock]. If a file is selected, its base directory will be returned using [method String.get_base_dir] instead.
</description>
</method>
+ <method name="get_current_feature_profile" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the name of the currently activated feature profile. If the default profile is currently active, an empty string is returned instead.
+ In order to get a reference to the [EditorFeatureProfile], you must load the feature profile using [method EditorFeatureProfile.load_from_file].
+ [b]Note:[/b] Feature profiles created via the user interface are loaded from the [code]feature_profiles[/code] directory, as a file with the [code].profile[/code] extension. The editor configuration folder can be found by using [method EditorPaths.get_config_dir].
+ </description>
+ </method>
<method name="get_current_path" qualifiers="const">
<return type="String" />
<description>
@@ -262,10 +270,16 @@
Restarts the editor. This closes the editor and then opens the same project. If [param save] is [code]true[/code], the project will be saved before restarting.
</description>
</method>
+ <method name="save_all_scenes">
+ <return type="void" />
+ <description>
+ Saves all opened scenes in the editor.
+ </description>
+ </method>
<method name="save_scene">
<return type="int" enum="Error" />
<description>
- Saves the scene. Returns either [constant OK] or [constant ERR_CANT_CREATE].
+ Saves the currently active scene. Returns either [constant OK] or [constant ERR_CANT_CREATE].
</description>
</method>
<method name="save_scene_as">
@@ -273,7 +287,7 @@
<param index="0" name="path" type="String" />
<param index="1" name="with_preview" type="bool" default="true" />
<description>
- Saves the scene as a file at [param path].
+ Saves the currently active scene as a file at [param path].
</description>
</method>
<method name="select_file">
@@ -283,6 +297,15 @@
Selects the file, with the path provided by [param file], in the FileSystem dock.
</description>
</method>
+ <method name="set_current_feature_profile">
+ <return type="void" />
+ <param index="0" name="profile_name" type="String" />
+ <description>
+ Selects and activates the specified feature profile with the given [param profile_name]. Set [param profile_name] to an empty string to reset to the default feature profile.
+ A feature profile can be created programmatically using the [EditorFeatureProfile] class.
+ [b]Note:[/b] The feature profile that gets activated must be located in the [code]feature_profiles[/code] directory, as a file with the [code].profile[/code] extension. If a profile could not be found, an error occurs. The editor configuration folder can be found by using [method EditorPaths.get_config_dir].
+ </description>
+ </method>
<method name="set_main_screen_editor">
<return type="void" />
<param index="0" name="name" type="String" />
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 957b6d8e88..ffb4df25d3 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" />
@@ -298,6 +326,7 @@
<param index="0" name="object" type="Object" />
<description>
Implement this function if your plugin edits a specific type of object (Resource or Node). If you return [code]true[/code], then you will get the functions [method _edit] and [method _make_visible] called when the editor requests them. If you have declared the methods [method _forward_canvas_gui_input] and [method _forward_3d_gui_input] these will be called too.
+ [b]Note:[/b] Each plugin should handle only one type of objects at a time. If a plugin handes more types of objects and they are edited at the same time, it will result in errors.
</description>
</method>
<method name="_has_main_screen" qualifiers="virtual const">
@@ -541,6 +570,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 ca27538c1f..85efe4362e 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -232,14 +232,16 @@
<member name="editors/2d/bone_outline_color" type="Color" setter="" getter="">
The outline color to use for non-selected bones in the 2D skeleton editor. See also [member editors/2d/bone_selected_color].
</member>
- <member name="editors/2d/bone_outline_size" type="int" setter="" getter="">
+ <member name="editors/2d/bone_outline_size" type="float" setter="" getter="">
The outline size in the 2D skeleton editor (in pixels). See also [member editors/2d/bone_width].
+ [b]Note:[/b] Changes to this value only apply after modifying a [Bone2D] node in any way, or closing and reopening the scene.
</member>
<member name="editors/2d/bone_selected_color" type="Color" setter="" getter="">
The color to use for selected bones in the 2D skeleton editor. See also [member editors/2d/bone_outline_color].
</member>
- <member name="editors/2d/bone_width" type="int" setter="" getter="">
+ <member name="editors/2d/bone_width" type="float" setter="" getter="">
The bone width in the 2D skeleton editor (in pixels). See also [member editors/2d/bone_outline_size].
+ [b]Note:[/b] Changes to this value only apply after modifying a [Bone2D] node in any way, or closing and reopening the scene.
</member>
<member name="editors/2d/grid_color" type="Color" setter="" getter="">
The grid color to use in the 2D editor.
@@ -250,6 +252,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>
@@ -384,9 +389,6 @@
<member name="editors/animation/autorename_animation_tracks" type="bool" setter="" getter="">
If [code]true[/code], automatically updates animation tracks' target paths when renaming or reparenting nodes in the Scene tree dock.
</member>
- <member name="editors/animation/confirm_insert_track" type="bool" setter="" getter="">
- If [code]true[/code], display a confirmation dialog when adding a new track to an animation by pressing the "key" icon next to a property.
- </member>
<member name="editors/animation/default_create_bezier_tracks" type="bool" setter="" getter="">
If [code]true[/code], create a Bezier track instead of a standard track when pressing the "key" icon next to a property. Bezier tracks provide more control over animation curves, but are more difficult to adjust quickly.
</member>
@@ -837,6 +839,9 @@
<member name="text_editor/completion/code_complete_delay" type="float" setter="" getter="">
The delay in seconds after which autocompletion suggestions should be displayed when the user stops typing.
</member>
+ <member name="text_editor/completion/code_complete_enabled" type="bool" setter="" getter="">
+ If [code]true[/code], code completion will be triggered automatically after [member text_editor/completion/code_complete_delay]. If [code]false[/code], you can still trigger completion manually by pressing [kbd]Ctrl + Space[/kbd] ([kbd]Cmd + Space[/kbd] on macOS).
+ </member>
<member name="text_editor/completion/complete_file_paths" type="bool" setter="" getter="">
If [code]true[/code], provides autocompletion suggestions for file paths in methods such as [code]load()[/code] and [code]preload()[/code].
</member>
diff --git a/doc/classes/EditorVCSInterface.xml b/doc/classes/EditorVCSInterface.xml
index 0acc53fa58..baf69f2c6d 100644
--- a/doc/classes/EditorVCSInterface.xml
+++ b/doc/classes/EditorVCSInterface.xml
@@ -240,7 +240,7 @@
<return type="void" />
<param index="0" name="msg" type="String" />
<description>
- Pops up an error message in the edior which is shown as coming from the underlying VCS. Use this to show VCS specific error messages.
+ Pops up an error message in the editor which is shown as coming from the underlying VCS. Use this to show VCS specific error messages.
</description>
</method>
</methods>
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/FontFile.xml b/doc/classes/FontFile.xml
index 2bd964053b..ba4761e900 100644
--- a/doc/classes/FontFile.xml
+++ b/doc/classes/FontFile.xml
@@ -134,7 +134,7 @@
<return type="int" />
<param index="0" name="cache_index" type="int" />
<description>
- Recturns an active face index in the TrueType / OpenType collection.
+ Returns an active face index in the TrueType / OpenType collection.
</description>
</method>
<method name="get_glyph_advance" qualifiers="const">
diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml
index c4193a5b01..2928215c3e 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">
@@ -49,7 +50,7 @@
Particle draw order. Uses [enum DrawOrder] values.
</member>
<member name="emitting" type="bool" setter="set_emitting" getter="is_emitting" default="true">
- If [code]true[/code], particles are being emitted.
+ If [code]true[/code], particles are being emitted. [member emitting] can be used to start and stop particles from emitting. However, if [member one_shot] is [code]true[/code] setting [member emitting] to [code]true[/code] will not restart the emission cycle until after all active particles finish processing. You can use the [signal finished] signal to be notified once all active particles finish processing.
</member>
<member name="explosiveness" type="float" setter="set_explosiveness_ratio" getter="get_explosiveness_ratio" default="0.0">
How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins.
@@ -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..4c0b2d12ed 100644
--- a/doc/classes/GPUParticles3D.xml
+++ b/doc/classes/GPUParticles3D.xml
@@ -78,7 +78,7 @@
<member name="draw_skin" type="Skin" setter="set_skin" getter="get_skin">
</member>
<member name="emitting" type="bool" setter="set_emitting" getter="is_emitting" default="true">
- If [code]true[/code], particles are being emitted.
+ If [code]true[/code], particles are being emitted. [member emitting] can be used to start and stop particles from emitting. However, if [member one_shot] is [code]true[/code] setting [member emitting] to [code]true[/code] will not restart the emission cycle until after all active particles finish processing. You can use the [signal finished] signal to be notified once all active particles finish processing.
</member>
<member name="explosiveness" type="float" setter="set_explosiveness_ratio" getter="get_explosiveness_ratio" default="0.0">
Time ratio between each emission. If [code]0[/code], particles are emitted continuously. If [code]1[/code], all particles are emitted simultaneously.
@@ -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/GraphEdit.xml b/doc/classes/GraphEdit.xml
index f6876ac21f..0aea0c5727 100644
--- a/doc/classes/GraphEdit.xml
+++ b/doc/classes/GraphEdit.xml
@@ -157,7 +157,7 @@
Returns an Array containing the list of connections. A connection consists in a structure of the form [code]{ from_port: 0, from: "GraphNode name 0", to_port: 1, to: "GraphNode name 1" }[/code].
</description>
</method>
- <method name="get_zoom_hbox">
+ <method name="get_menu_hbox">
<return type="HBoxContainer" />
<description>
Gets the [HBoxContainer] that contains the zooming and grid snap controls in the top left of the graph. You can use this method to reposition the toolbar or to add your own custom controls to it.
@@ -255,16 +255,19 @@
<member name="right_disconnects" type="bool" setter="set_right_disconnects" getter="is_right_disconnects_enabled" default="false">
If [code]true[/code], enables disconnection of existing connections in the GraphEdit by dragging the right end.
</member>
- <member name="scroll_offset" type="Vector2" setter="set_scroll_ofs" getter="get_scroll_ofs" default="Vector2(0, 0)">
+ <member name="scroll_offset" type="Vector2" setter="set_scroll_offset" getter="get_scroll_offset" default="Vector2(0, 0)">
The scroll offset.
</member>
+ <member name="show_grid" type="bool" setter="set_show_grid" getter="is_showing_grid" default="true">
+ If [code]true[/code], the grid is visible.
+ </member>
<member name="show_zoom_label" type="bool" setter="set_show_zoom_label" getter="is_showing_zoom_label" default="false">
If [code]true[/code], makes a label with the current zoom level visible. The zoom value is displayed in percents.
</member>
- <member name="snap_distance" type="int" setter="set_snap" getter="get_snap" default="20">
- The snapping distance in pixels.
+ <member name="snapping_distance" type="int" setter="set_snapping_distance" getter="get_snapping_distance" default="20">
+ The snapping distance in pixels, also determines the grid line distance.
</member>
- <member name="use_snap" type="bool" setter="set_use_snap" getter="is_using_snap" default="true">
+ <member name="snapping_enabled" type="bool" setter="set_snapping_enabled" getter="is_snapping_enabled" default="true">
If [code]true[/code], enables snapping.
</member>
<member name="zoom" type="float" setter="set_zoom" getter="get_zoom" default="1.0">
@@ -412,23 +415,28 @@
<theme_item name="port_hotzone_outer_extent" data_type="constant" type="int" default="26">
The horizontal range within which a port can be grabbed (outer side).
</theme_item>
+ <theme_item name="grid_toggle" data_type="icon" type="Texture2D">
+ The icon for the grid toggle button.
+ </theme_item>
<theme_item name="layout" data_type="icon" type="Texture2D">
+ The icon for the layout button for auto-arranging the graph.
</theme_item>
- <theme_item name="minimap" data_type="icon" type="Texture2D">
+ <theme_item name="minimap_toggle" data_type="icon" type="Texture2D">
+ The icon for the minimap toggle button.
</theme_item>
- <theme_item name="minus" data_type="icon" type="Texture2D">
- The icon for the zoom out button.
+ <theme_item name="snapping_toggle" data_type="icon" type="Texture2D">
+ The icon for the snapping toggle button.
</theme_item>
- <theme_item name="more" data_type="icon" type="Texture2D">
+ <theme_item name="zoom_in" data_type="icon" type="Texture2D">
The icon for the zoom in button.
</theme_item>
- <theme_item name="reset" data_type="icon" type="Texture2D">
- The icon for the zoom reset button.
+ <theme_item name="zoom_out" data_type="icon" type="Texture2D">
+ The icon for the zoom out button.
</theme_item>
- <theme_item name="snap" data_type="icon" type="Texture2D">
- The icon for the snap toggle button.
+ <theme_item name="zoom_reset" data_type="icon" type="Texture2D">
+ The icon for the zoom reset button.
</theme_item>
- <theme_item name="bg" data_type="style" type="StyleBox">
+ <theme_item name="panel" data_type="style" type="StyleBox">
The background drawn under the grid.
</theme_item>
</theme_items>
diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml
index e0dacb2a79..d160141842 100644
--- a/doc/classes/GraphNode.xml
+++ b/doc/classes/GraphNode.xml
@@ -236,9 +236,6 @@
</method>
</methods>
<members>
- <member name="comment" type="bool" setter="set_comment" getter="is_comment" default="false">
- If [code]true[/code], the GraphNode is a comment node.
- </member>
<member name="draggable" type="bool" setter="set_draggable" getter="is_draggable" default="true">
If [code]true[/code], the user can drag the GraphNode.
</member>
@@ -373,12 +370,6 @@
<theme_item name="breakpoint" data_type="style" type="StyleBox">
The background used when [member overlay] is set to [constant OVERLAY_BREAKPOINT].
</theme_item>
- <theme_item name="comment" data_type="style" type="StyleBox">
- The [StyleBox] used when [member comment] is enabled.
- </theme_item>
- <theme_item name="comment_focus" data_type="style" type="StyleBox">
- The [StyleBox] used when [member comment] is enabled and the [GraphNode] is focused.
- </theme_item>
<theme_item name="frame" data_type="style" type="StyleBox">
The default background for [GraphNode].
</theme_item>
diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml
index 9a8207d1fc..f0385a7814 100644
--- a/doc/classes/HTTPClient.xml
+++ b/doc/classes/HTTPClient.xml
@@ -41,6 +41,7 @@
<description>
Returns the response's body length.
[b]Note:[/b] Some Web servers may not send a body length. In this case, the value returned will be [code]-1[/code]. If using chunked transfer encoding, the body length will also be [code]-1[/code].
+ [b]Note:[/b] This function always returns [code]-1[/code] on the Web platform due to browsers limitations.
</description>
</method>
<method name="get_response_code" qualifiers="const">
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index a08f1da77a..1486990995 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -312,6 +312,13 @@
[b]Note:[/b] Godot's BMP module doesn't support 16-bit per pixel images. Only 1-bit, 4-bit, 8-bit, 24-bit, and 32-bit per pixel images are supported.
</description>
</method>
+ <method name="load_dds_from_buffer">
+ <return type="int" enum="Error" />
+ <param index="0" name="buffer" type="PackedByteArray" />
+ <description>
+ Loads an image from the binary contents of a DDS file.
+ </description>
+ </method>
<method name="load_from_file" qualifiers="static">
<return type="Image" />
<param index="0" name="path" type="String" />
@@ -333,6 +340,25 @@
Loads an image from the binary contents of a PNG file.
</description>
</method>
+ <method name="load_svg_from_buffer">
+ <return type="int" enum="Error" />
+ <param index="0" name="buffer" type="PackedByteArray" />
+ <param index="1" name="scale" type="float" default="1.0" />
+ <description>
+ Loads an image from the UTF-8 binary contents of an [b]uncompressed[/b] SVG file ([b].svg[/b]).
+ [b]Note:[/b] Beware when using compressed SVG files (like [b].svgz[/b]), they need to be [code]decompressed[/code] before loading.
+ [b]Note:[/b] This method is only available in engine builds with the SVG module enabled. By default, the SVG module is enabled, but it can be disabled at build-time using the [code]module_svg_enabled=no[/code] SCons option.
+ </description>
+ </method>
+ <method name="load_svg_from_string">
+ <return type="int" enum="Error" />
+ <param index="0" name="svg_str" type="String" />
+ <param index="1" name="scale" type="float" default="1.0" />
+ <description>
+ Loads an image from the string contents of a SVG file ([b].svg[/b]).
+ [b]Note:[/b] This method is only available in engine builds with the SVG module enabled. By default, the SVG module is enabled, but it can be disabled at build-time using the [code]module_svg_enabled=no[/code] SCons option.
+ </description>
+ </method>
<method name="load_tga_from_buffer">
<return type="int" enum="Error" />
<param index="0" name="buffer" type="PackedByteArray" />
@@ -660,13 +686,13 @@
<constant name="FORMAT_DXT5_RA_AS_RG" value="34" enum="Format">
</constant>
<constant name="FORMAT_ASTC_4x4" value="35" enum="Format">
- [url=https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression]Adaptive Scalable Texutre Compression[/url]. This implements the 4x4 (high quality) mode.
+ [url=https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression]Adaptive Scalable Texture Compression[/url]. This implements the 4x4 (high quality) mode.
</constant>
<constant name="FORMAT_ASTC_4x4_HDR" value="36" enum="Format">
Same format as [constant FORMAT_ASTC_4x4], but with the hint to let the GPU know it is used for HDR.
</constant>
<constant name="FORMAT_ASTC_8x8" value="37" enum="Format">
- [url=https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression]Adaptive Scalable Texutre Compression[/url]. This implements the 8x8 (low quality) mode.
+ [url=https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression]Adaptive Scalable Texture Compression[/url]. This implements the 8x8 (low quality) mode.
</constant>
<constant name="FORMAT_ASTC_8x8_HDR" value="38" enum="Format">
Same format as [constant FORMAT_ASTC_8x8], but with the hint to let the GPU know it is used for HDR.
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index 5cefdaf415..c2a20827d2 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -343,6 +343,15 @@
[b]Note:[/b] This value can be immediately overwritten by the hardware sensor value on Android and iOS.
</description>
</method>
+ <method name="should_ignore_device" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="vendor_id" type="int" />
+ <param index="1" name="product_id" type="int" />
+ <description>
+ Queries whether an input device should be ignored or not. Devices can be ignored by setting the environment variable [code]SDL_GAMECONTROLLER_IGNORE_DEVICES[/code]. Read the [url=https://wiki.libsdl.org/SDL2]SDL documentation[/url] for more information.
+ [b]Note:[/b] Some 3rd party tools can contribute to the list of ignored devices. For example, [i]SteamInput[/i] creates virtual devices from physical devices for remapping purposes. To avoid handling the same input device twice, the original device is added to the ignore list.
+ </description>
+ </method>
<method name="start_joy_vibration">
<return type="void" />
<param index="0" name="device" type="int" />
diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml
index 6c4242b92d..6448ace9ba 100644
--- a/doc/classes/Mesh.xml
+++ b/doc/classes/Mesh.xml
@@ -253,7 +253,7 @@
[PackedFloat32Array] or [PackedFloat64Array] of bone weights in the range [code]0.0[/code] to [code]1.0[/code] (inclusive). Contains either 4 or 8 numbers per vertex depending on the presence of the [constant ARRAY_FLAG_USE_8_BONE_WEIGHTS] flag.
</constant>
<constant name="ARRAY_INDEX" value="12" enum="ArrayType">
- [PackedInt32Array] of integers used as indices referencing vertices, colors, normals, tangents, and textures. All of those arrays must have the same number of elements as the vertex array. No index can be beyond the vertex array size. When this index array is present, it puts the function into "index mode," where the index selects the *i*'th vertex, normal, tangent, color, UV, etc. This means if you want to have different normals or colors along an edge, you have to duplicate the vertices.
+ [PackedInt32Array] of integers used as indices referencing vertices, colors, normals, tangents, and textures. All of those arrays must have the same number of elements as the vertex array. No index can be beyond the vertex array size. When this index array is present, it puts the function into "index mode," where the index selects the [i]i[/i]'th vertex, normal, tangent, color, UV, etc. This means if you want to have different normals or colors along an edge, you have to duplicate the vertices.
For triangles, the index array is interpreted as triples, referring to the vertices of each triangle. For lines, the index array is in pairs indicating the start and end of each line.
</constant>
<constant name="ARRAY_MAX" value="13" enum="ArrayType">
diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml
index 8bb1ff1dae..c1570f3149 100644
--- a/doc/classes/NavigationAgent2D.xml
+++ b/doc/classes/NavigationAgent2D.xml
@@ -53,7 +53,7 @@
<method name="get_final_position">
<return type="Vector2" />
<description>
- Returns the reachable final position of the current navigation path in global coordinates. This can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
+ Returns the reachable final position of the current navigation path in global coordinates. This position can change if the agent needs to update the navigation path which makes the agent emit the [signal path_changed] signal.
</description>
</method>
<method name="get_navigation_layer_value" qualifiers="const">
@@ -84,13 +84,14 @@
<method name="is_navigation_finished">
<return type="bool" />
<description>
- Returns true if the navigation path's final position has been reached.
+ Returns [code]true[/code] if the end of the currently loaded navigation path has been reached.
+ [b]Note:[/b] While true prefer to stop calling update functions like [method get_next_path_position]. This avoids jittering the standing agent due to calling repeated path updates.
</description>
</method>
<method name="is_target_reachable">
<return type="bool" />
<description>
- Returns true if [member target_position] is reachable. The target position is set using [member target_position].
+ Returns [code]true[/code] if [method get_final_position] is within [member target_desired_distance] of the [member target_position].
</description>
</method>
<method name="is_target_reached" qualifiers="const">
@@ -229,17 +230,20 @@
</signal>
<signal name="navigation_finished">
<description>
- Notifies when the final position is reached.
+ Emitted once per loaded path when the agent internal navigation path index reaches the last index of the loaded path array. The agent internal navigation path index can be received with [method get_current_navigation_path_index].
</description>
</signal>
<signal name="path_changed">
<description>
- Notifies when the navigation path changes.
+ Emitted when the agent had to update the loaded path:
+ - because path was previously empty.
+ - because navigation map has changed.
+ - because agent pushed further away from the current path segment than the [member path_max_distance].
</description>
</signal>
<signal name="target_reached">
<description>
- Notifies when the player-defined [member target_position] is reached.
+ Emitted once per loaded path when the agent's global position is the first time within [member target_desired_distance] to the [member target_position].
</description>
</signal>
<signal name="velocity_computed">
diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml
index 448984a397..aef9b0ac56 100644
--- a/doc/classes/NavigationAgent3D.xml
+++ b/doc/classes/NavigationAgent3D.xml
@@ -53,7 +53,7 @@
<method name="get_final_position">
<return type="Vector3" />
<description>
- Returns the reachable final position of the current navigation path in global coordinates. This position can change if the navigation path is altered in any way. Because of this, it would be best to check this each frame.
+ Returns the reachable final position of the current navigation path in global coordinates. This position can change if the agent needs to update the navigation path which makes the agent emit the [signal path_changed] signal.
</description>
</method>
<method name="get_navigation_layer_value" qualifiers="const">
@@ -84,13 +84,14 @@
<method name="is_navigation_finished">
<return type="bool" />
<description>
- Returns true if the navigation path's final position has been reached.
+ Returns [code]true[/code] if the end of the currently loaded navigation path has been reached.
+ [b]Note:[/b] While true prefer to stop calling update functions like [method get_next_path_position]. This avoids jittering the standing agent due to calling repeated path updates.
</description>
</method>
<method name="is_target_reachable">
<return type="bool" />
<description>
- Returns true if [member target_position] is reachable. The target position is set using [member target_position].
+ Returns [code]true[/code] if [method get_final_position] is within [member target_desired_distance] of the [member target_position].
</description>
</method>
<method name="is_target_reached" qualifiers="const">
@@ -230,23 +231,26 @@
- [code]type[/code]: Always [constant NavigationPathQueryResult3D.PATH_SEGMENT_TYPE_LINK].
- [code]rid[/code]: The [RID] of the link.
- [code]owner[/code]: The object which manages the link (usually [NavigationLink3D]).
- - [code]link_entry_position[/code]: If [code]owner[/code] is available and the owner is a [NavigationLink2D], it will contain the global position of the link's point the agent is entering.
- - [code]link_exit_position[/code]: If [code]owner[/code] is available and the owner is a [NavigationLink2D], it will contain the global position of the link's point which the agent is exiting.
+ - [code]link_entry_position[/code]: If [code]owner[/code] is available and the owner is a [NavigationLink3D], it will contain the global position of the link's point the agent is entering.
+ - [code]link_exit_position[/code]: If [code]owner[/code] is available and the owner is a [NavigationLink3D], it will contain the global position of the link's point which the agent is exiting.
</description>
</signal>
<signal name="navigation_finished">
<description>
- Notifies when the final position is reached.
+ Emitted once per loaded path when the agent internal navigation path index reaches the last index of the loaded path array. The agent internal navigation path index can be received with [method get_current_navigation_path_index].
</description>
</signal>
<signal name="path_changed">
<description>
- Notifies when the navigation path changes.
+ Emitted when the agent had to update the loaded path:
+ - because path was previously empty.
+ - because navigation map has changed.
+ - because agent pushed further away from the current path segment than the [member path_max_distance].
</description>
</signal>
<signal name="target_reached">
<description>
- Notifies when the player-defined [member target_position] is reached.
+ Emitted once per loaded path when the agent's global position is the first time within [member target_desired_distance] to the [member target_position].
</description>
</signal>
<signal name="velocity_computed">
diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml
index 3dcda01f76..464ffd50af 100644
--- a/doc/classes/NavigationMesh.xml
+++ b/doc/classes/NavigationMesh.xml
@@ -18,6 +18,12 @@
Adds a polygon using the indices of the vertices you get when calling [method get_vertices].
</description>
</method>
+ <method name="clear">
+ <return type="void" />
+ <description>
+ Clears the internal arrays for vertices and polygon indices.
+ </description>
+ </method>
<method name="clear_polygons">
<return type="void" />
<description>
@@ -105,8 +111,8 @@
<member name="edge_max_error" type="float" setter="set_edge_max_error" getter="get_edge_max_error" default="1.3">
The maximum distance a simplified contour's border edges should deviate the original raw contour.
</member>
- <member name="edge_max_length" type="float" setter="set_edge_max_length" getter="get_edge_max_length" default="12.0">
- The maximum allowed length for contour edges along the border of the mesh.
+ <member name="edge_max_length" type="float" setter="set_edge_max_length" getter="get_edge_max_length" default="0.0">
+ The maximum allowed length for contour edges along the border of the mesh. A value of [code]0.0[/code] disables this feature.
[b]Note:[/b] While baking, this value will be rounded up to the nearest multiple of [member cell_size].
</member>
<member name="filter_baking_aabb" type="AABB" setter="set_filter_baking_aabb" getter="get_filter_baking_aabb" default="AABB(0, 0, 0, 0, 0, 0)">
diff --git a/doc/classes/NavigationMeshSourceGeometryData3D.xml b/doc/classes/NavigationMeshSourceGeometryData3D.xml
index bf57baeb44..dc446dcaaa 100644
--- a/doc/classes/NavigationMeshSourceGeometryData3D.xml
+++ b/doc/classes/NavigationMeshSourceGeometryData3D.xml
@@ -54,7 +54,7 @@
<method name="has_data">
<return type="bool" />
<description>
- Returns [b]true[/b] when parsed source geometry data exists.
+ Returns [code]true[/code] when parsed source geometry data exists.
</description>
</method>
<method name="set_indices">
diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml
index 1c44ffbd57..fe28c5a468 100644
--- a/doc/classes/NavigationPolygon.xml
+++ b/doc/classes/NavigationPolygon.xml
@@ -69,6 +69,12 @@
Adds a polygon using the indices of the vertices you get when calling [method get_vertices].
</description>
</method>
+ <method name="clear">
+ <return type="void" />
+ <description>
+ Clears the internal arrays for vertices and polygon indices.
+ </description>
+ </method>
<method name="clear_outlines">
<return type="void" />
<description>
diff --git a/doc/classes/NavigationRegion2D.xml b/doc/classes/NavigationRegion2D.xml
index 0ac7fc8bc2..acd789cc22 100644
--- a/doc/classes/NavigationRegion2D.xml
+++ b/doc/classes/NavigationRegion2D.xml
@@ -30,6 +30,12 @@
Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="get_navigation_map" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the current navigation map [RID] used by this region.
+ </description>
+ </method>
<method name="get_region_rid" qualifiers="const">
<return type="RID" />
<description>
@@ -52,6 +58,13 @@
Based on [param value], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="set_navigation_map">
+ <return type="void" />
+ <param index="0" name="navigation_map" type="RID" />
+ <description>
+ Sets the [RID] of the navigation map this region should use. By default the region will automatically join the [World2D] default navigation map so this function is only required to override the default map.
+ </description>
+ </method>
</methods>
<members>
<member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
diff --git a/doc/classes/NavigationRegion3D.xml b/doc/classes/NavigationRegion3D.xml
index 79cb55f870..7e8ead2032 100644
--- a/doc/classes/NavigationRegion3D.xml
+++ b/doc/classes/NavigationRegion3D.xml
@@ -30,6 +30,12 @@
Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="get_navigation_map" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the current navigation map [RID] used by this region.
+ </description>
+ </method>
<method name="get_region_rid" qualifiers="const">
<return type="RID" />
<description>
@@ -44,6 +50,13 @@
Based on [param value], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="set_navigation_map">
+ <return type="void" />
+ <param index="0" name="navigation_map" type="RID" />
+ <description>
+ Sets the [RID] of the navigation map this region should use. By default the region will automatically join the [World3D] default navigation map so this function is only required to override the default map.
+ </description>
+ </method>
</methods>
<members>
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml
index 7f0e29676b..a52c9abefd 100644
--- a/doc/classes/NavigationServer2D.xml
+++ b/doc/classes/NavigationServer2D.xml
@@ -66,7 +66,7 @@
<param index="0" name="agent" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] is [code]true[/code] the specified [param agent] uses avoidance.
+ If [param enabled] is [code]true[/code], the specified [param agent] uses avoidance.
</description>
</method>
<method name="agent_set_avoidance_layers">
@@ -207,6 +207,13 @@
Create a new link between two positions on a map.
</description>
</method>
+ <method name="link_get_enabled" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="link" type="RID" />
+ <description>
+ Returns [code]true[/code] if the specified [param link] is enabled.
+ </description>
+ </method>
<method name="link_get_end_position" qualifiers="const">
<return type="Vector2" />
<param index="0" name="link" type="RID" />
@@ -271,6 +278,14 @@
Sets whether this [param link] can be travelled in both directions.
</description>
</method>
+ <method name="link_set_enabled">
+ <return type="void" />
+ <param index="0" name="link" type="RID" />
+ <param index="1" name="enabled" type="bool" />
+ <description>
+ If [param enabled] is [code]true[/code], the specified [param link] will contribute to its current navigation map.
+ </description>
+ </method>
<method name="link_set_end_position">
<return type="void" />
<param index="0" name="link" type="RID" />
@@ -470,7 +485,7 @@
<param index="0" name="map" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- Set the navigation [param map] edge connection use. If [param enabled] the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ Set the navigation [param map] edge connection use. If [param enabled] is [code]true[/code], the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</description>
</method>
<method name="obstacle_create">
@@ -505,7 +520,7 @@
<param index="0" name="obstacle" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] the provided [param obstacle] affects avoidance using agents.
+ If [param enabled] is [code]true[/code], the provided [param obstacle] affects avoidance using agents.
</description>
</method>
<method name="obstacle_set_avoidance_layers">
@@ -601,6 +616,13 @@
Returns how many connections this [param region] has with other regions in the map.
</description>
</method>
+ <method name="region_get_enabled" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="region" type="RID" />
+ <description>
+ Returns [code]true[/code] if the specified [param region] is enabled.
+ </description>
+ </method>
<method name="region_get_enter_cost" qualifiers="const">
<return type="float" />
<param index="0" name="region" type="RID" />
@@ -653,6 +675,14 @@
[b]Note:[/b] If navigation meshes from different navigation regions overlap (which should be avoided in general) the result might not be what is expected.
</description>
</method>
+ <method name="region_set_enabled">
+ <return type="void" />
+ <param index="0" name="region" type="RID" />
+ <param index="1" name="enabled" type="bool" />
+ <description>
+ If [param enabled] is [code]true[/code] the specified [param region] will contribute to its current navigation map.
+ </description>
+ </method>
<method name="region_set_enter_cost">
<return type="void" />
<param index="0" name="region" type="RID" />
@@ -714,7 +744,7 @@
<param index="0" name="region" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ If [param enabled] is [code]true[/code], the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</description>
</method>
<method name="set_debug_enabled">
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index 7dc3179e6b..86c605b8a3 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -4,7 +4,7 @@
A server interface for low-level 3D navigation access.
</brief_description>
<description>
- NavigationServer2D is the server that handles navigation maps, regions and agents. It does not handle A* navigation from [AStar3D].
+ NavigationServer3D is the server that handles navigation maps, regions and agents. It does not handle A* navigation from [AStar3D].
Maps are made up of regions, which are made of navigation meshes. Together, they define the navigable areas in the 3D world.
[b]Note:[/b] Most [NavigationServer3D] changes take effect after the next physics frame and not immediately. This includes all changes made to maps, regions or agents by navigation-related nodes in the scene tree or made through scripts.
For two regions to be connected to each other, they must share a similar edge. An edge is considered connected to another if both of its two vertices are at a distance less than [code]edge_connection_margin[/code] to the respective other edge's vertex.
@@ -73,7 +73,7 @@
<param index="0" name="agent" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] the provided [param agent] calculates avoidance.
+ If [param enabled] is [code]true[/code], the provided [param agent] calculates avoidance.
</description>
</method>
<method name="agent_set_avoidance_layers">
@@ -248,6 +248,13 @@
Create a new link between two positions on a map.
</description>
</method>
+ <method name="link_get_enabled" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="link" type="RID" />
+ <description>
+ Returns [code]true[/code] if the specified [param link] is enabled.
+ </description>
+ </method>
<method name="link_get_end_position" qualifiers="const">
<return type="Vector3" />
<param index="0" name="link" type="RID" />
@@ -312,6 +319,14 @@
Sets whether this [param link] can be travelled in both directions.
</description>
</method>
+ <method name="link_set_enabled">
+ <return type="void" />
+ <param index="0" name="link" type="RID" />
+ <param index="1" name="enabled" type="bool" />
+ <description>
+ If [param enabled] is [code]true[/code], the specified [param link] will contribute to its current navigation map.
+ </description>
+ </method>
<method name="link_set_end_position">
<return type="void" />
<param index="0" name="link" type="RID" />
@@ -559,7 +574,7 @@
<param index="0" name="map" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- Set the navigation [param map] edge connection use. If [param enabled] the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ Set the navigation [param map] edge connection use. If [param enabled] is [code]true[/code], the navigation map allows navigation regions to use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</description>
</method>
<method name="obstacle_create">
@@ -601,7 +616,7 @@
<param index="0" name="obstacle" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] the provided [param obstacle] affects avoidance using agents.
+ If [param enabled] is [code]true[/code], the provided [param obstacle] affects avoidance using agents.
</description>
</method>
<method name="obstacle_set_avoidance_layers">
@@ -696,12 +711,13 @@
Queries a path in a given navigation map. Start and target position and other parameters are defined through [NavigationPathQueryParameters3D]. Updates the provided [NavigationPathQueryResult3D] result object with the path among other results requested by the query.
</description>
</method>
- <method name="region_bake_navigation_mesh">
+ <method name="region_bake_navigation_mesh" is_deprecated="true">
<return type="void" />
<param index="0" name="navigation_mesh" type="NavigationMesh" />
<param index="1" name="root_node" type="Node" />
<description>
Bakes the [param navigation_mesh] with bake source geometry collected starting from the [param root_node].
+ [i]Deprecated.[/i] This function is deprecated due to core threading changes. To upgrade existing code, first create a [NavigationMeshSourceGeometryData3D] resource. Use this resource with [method parse_source_geometry_data] to parse the SceneTree for nodes that should contribute to the navigation mesh baking. The SceneTree parsing needs to happen on the main thread. After the parsing is finished use the resource with [method bake_from_source_geometry_data] to bake a navigation mesh.
</description>
</method>
<method name="region_create">
@@ -733,6 +749,13 @@
Returns how many connections this [param region] has with other regions in the map.
</description>
</method>
+ <method name="region_get_enabled" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="region" type="RID" />
+ <description>
+ Returns [code]true[/code] if the specified [param region] is enabled.
+ </description>
+ </method>
<method name="region_get_enter_cost" qualifiers="const">
<return type="float" />
<param index="0" name="region" type="RID" />
@@ -785,6 +808,14 @@
[b]Note:[/b] If navigation meshes from different navigation regions overlap (which should be avoided in general) the result might not be what is expected.
</description>
</method>
+ <method name="region_set_enabled">
+ <return type="void" />
+ <param index="0" name="region" type="RID" />
+ <param index="1" name="enabled" type="bool" />
+ <description>
+ If [param enabled] is [code]true[/code], the specified [param region] will contribute to its current navigation map.
+ </description>
+ </method>
<method name="region_set_enter_cost">
<return type="void" />
<param index="0" name="region" type="RID" />
@@ -846,7 +877,7 @@
<param index="0" name="region" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [param enabled] the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
+ If [param enabled] is [code]true[/code], the navigation [param region] will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin.
</description>
</method>
<method name="set_active">
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 58c85abe1c..49ab3918bb 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -761,8 +761,8 @@
<param index="0" name="id" type="int" />
<param index="1" name="recursive" type="bool" default="true" />
<description>
- Sets the node's multiplayer authority to the peer with the given peer ID. The multiplayer authority is the peer that has authority over the node on the network. Useful in conjunction with [method rpc_config] and the [MultiplayerAPI]. Inherited from the parent node by default, which ultimately defaults to peer ID 1 (the server). If [param recursive], the given peer is recursively set as the authority for all children of this node.
- [b]Warning:[/b] This does [b]not[/b] automatically replicate the new authority to other peers. It is developer's responsibility to do so. You can propagate the information about the new authority using [member MultiplayerSpawner.spawn_function], an RPC, or using a [MultiplayerSynchronizer].
+ Sets the node's multiplayer authority to the peer with the given peer ID. The multiplayer authority is the peer that has authority over the node on the network. Useful in conjunction with [method rpc_config] and the [MultiplayerAPI]. Defaults to peer ID 1 (the server). If [param recursive], the given peer is recursively set as the authority for all children of this node.
+ [b]Warning:[/b] This does [b]not[/b] automatically replicate the new authority to other peers. It is developer's responsibility to do so. You can propagate the information about the new authority using [member MultiplayerSpawner.spawn_function], an RPC, or using a [MultiplayerSynchronizer]. Also, the parent's authority does [b]not[/b] propagate to newly added children.
</description>
</method>
<method name="set_physics_process">
@@ -858,8 +858,8 @@
[b]Note:[/b] Auto-generated names might include the [code]@[/code] character, which is reserved for unique names when using [method add_child]. When setting the name manually, any [code]@[/code] will be removed.
</member>
<member name="owner" type="Node" setter="set_owner" getter="get_owner">
- The node owner. A node can have any other node as owner (as long as it is a valid parent, grandparent, etc. ascending in the tree). When saving a node (using [PackedScene]), all the nodes it owns will be saved with it. This allows for the creation of complex [SceneTree]s, with instancing and subinstancing.
- [b]Note:[/b] If you want a child to be persisted to a [PackedScene], you must set [member owner] in addition to calling [method add_child]. This is typically relevant for [url=$DOCS_URL/tutorials/plugins/running_code_in_the_editor.html]tool scripts[/url] and [url=$DOCS_URL/tutorials/plugins/editor/index.html]editor plugins[/url]. If [method add_child] is called without setting [member owner], the newly added [Node] will not be visible in the scene tree, though it will be visible in the 2D/3D view.
+ The node owner. A node can have any ancestor node as owner (i.e. a parent, grandparent, etc. node ascending in the tree). This implies that [method add_child] should be called before setting the owner, so that this relationship of parenting exists. When saving a node (using [PackedScene]), all the nodes it owns will be saved with it. This allows for the creation of complex scene trees, with instancing and subinstancing.
+ [b]Note:[/b] If you want a child to be persisted to a [PackedScene], you must set [member owner] in addition to calling [method add_child]. This is typically relevant for [url=$DOCS_URL/tutorials/plugins/running_code_in_the_editor.html]tool scripts[/url] and [url=$DOCS_URL/tutorials/plugins/editor/index.html]editor plugins[/url]. If a new node is added to the tree without setting its owner as an ancestor in that tree, it will be visible in the 2D/3D view, but not in the scene tree (and not persisted when packing or saving).
</member>
<member name="process_mode" type="int" setter="set_process_mode" getter="get_process_mode" enum="Node.ProcessMode" default="0">
Can be used to pause or unpause the node, or make the node paused based on the [SceneTree], or make it inherit the process mode from its parent (default).
@@ -1052,10 +1052,10 @@
Notification received from the OS when the screen's DPI has been changed. Only implemented on macOS.
</constant>
<constant name="NOTIFICATION_VP_MOUSE_ENTER" value="1010">
- Notification received when the mouse enters the viewport.
+ Notification received when the mouse cursor enters the [Viewport]'s visible area, that is not occluded behind other [Control]s or [Window]s, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not.
</constant>
<constant name="NOTIFICATION_VP_MOUSE_EXIT" value="1011">
- Notification received when the mouse leaves the viewport.
+ Notification received when the mouse cursor leaves the [Viewport]'s visible area, that is not occluded behind other [Control]s or [Window]s, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not.
</constant>
<constant name="NOTIFICATION_OS_MEMORY_WARNING" value="2009">
Notification received from the OS when the application is exceeding its allocated memory.
diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml
index ea4c6366f2..b5ead07a10 100644
--- a/doc/classes/Node3D.xml
+++ b/doc/classes/Node3D.xml
@@ -291,7 +291,7 @@
Access to the node rotation as a [Quaternion]. This property is ideal for tweening complex rotations.
</member>
<member name="rotation" type="Vector3" setter="set_rotation" getter="get_rotation" default="Vector3(0, 0, 0)">
- Rotation part of the local transformation in radians, specified in terms of Euler angles. The angles construct a rotaton in the order specified by the [member rotation_order] property.
+ Rotation part of the local transformation in radians, specified in terms of Euler angles. The angles construct a rotation in the order specified by the [member rotation_order] property.
[b]Note:[/b] In the mathematical sense, rotation is a matrix and not a vector. The three Euler angles, which are the three independent parameters of the Euler-angle parametrization of the rotation matrix, are stored in a [Vector3] data structure not because the rotation is a vector, but only because [Vector3] exists as a convenient data-structure to store 3 floating-point numbers. Therefore, applying affine operations on the rotation "vector" is not meaningful.
[b]Note:[/b] This property is edited in the inspector in degrees. If you want to use degrees in a script, use [member rotation_degrees].
</member>
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/OpenXRAPIExtension.xml b/doc/classes/OpenXRAPIExtension.xml
new file mode 100644
index 0000000000..181da916d1
--- /dev/null
+++ b/doc/classes/OpenXRAPIExtension.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="OpenXRAPIExtension" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Makes the OpenXR API available for GDExtension.
+ </brief_description>
+ <description>
+ [OpenXRAPIExtension] makes OpenXR available for GDExtension. It provides the OpenXR API to GDExtension through the [method get_instance_proc_addr] method, and the OpenXR instance through [method get_instance].
+ It also provides methods for querying the status of OpenXR initialization, and helper methods for ease of use of the API with GDExtension.
+ </description>
+ <tutorials>
+ <link title="XrResult documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrResult.html</link>
+ <link title="XrInstance documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrInstance.html</link>
+ <link title="XrSpace documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSpace.html</link>
+ <link title="XrSession documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSession.html</link>
+ <link title="XrSystemId documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSystemId.html</link>
+ <link title="xrBeginSession documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrBeginSession.html</link>
+ <link title="XrPosef documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrPosef.html</link>
+ </tutorials>
+ <methods>
+ <method name="can_render">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if OpenXR is initialized for rendering with an XR viewport.
+ </description>
+ </method>
+ <method name="get_error_string">
+ <return type="String" />
+ <param index="0" name="result" type="int" />
+ <description>
+ Returns an error string for the given [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrResult.html]XrResult[/url].
+ </description>
+ </method>
+ <method name="get_instance">
+ <return type="int" />
+ <description>
+ Returns the [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrInstance.html]XrInstance[/url] created during the initialization of the OpenXR API.
+ </description>
+ </method>
+ <method name="get_instance_proc_addr">
+ <return type="int" />
+ <param index="0" name="name" type="String" />
+ <description>
+ Returns the function pointer of the OpenXR function with the specified name, cast to an integer. If the function with the given name does not exist, the method returns [code]0[/code].
+ [b]Note:[/b] [code]openxr/util.h[/code] contains utility macros for acquiring OpenXR functions, e.g. [code]GDEXTENSION_INIT_XR_FUNC_V(xrCreateAction)[/code].
+ </description>
+ </method>
+ <method name="get_next_frame_time">
+ <return type="int" />
+ <description>
+ Returns the timing for the next frame.
+ </description>
+ </method>
+ <method name="get_play_space">
+ <return type="int" />
+ <description>
+ Returns the play space, which is an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSpace.html]XrSpace[/url] cast to an integer.
+ </description>
+ </method>
+ <method name="get_session">
+ <return type="int" />
+ <description>
+ Returns the OpenXR session, which is an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSession.html]XrSession[/url] cast to an integer.
+ </description>
+ </method>
+ <method name="get_swapchain_format_name">
+ <return type="String" />
+ <param index="0" name="swapchain_format" type="int" />
+ <description>
+ Returns the name of the specified swapchain format.
+ </description>
+ </method>
+ <method name="get_system_id">
+ <return type="int" />
+ <description>
+ Returns the id of the system, which is a [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSystemId.html]XrSystemId[/url] cast to an integer.
+ </description>
+ </method>
+ <method name="is_initialized">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if OpenXR is initialized.
+ </description>
+ </method>
+ <method name="is_running">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if OpenXR is running ([url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/xrBeginSession.html]xrBeginSession[/url] was successfully called and the swapchains were created).
+ </description>
+ </method>
+ <method name="openxr_is_enabled" qualifiers="static">
+ <return type="bool" />
+ <param index="0" name="check_run_in_editor" type="bool" />
+ <description>
+ Returns [code]true[/code] if OpenXR is enabled.
+ </description>
+ </method>
+ <method name="transform_from_pose">
+ <return type="Transform3D" />
+ <param index="0" name="pose" type="const void*" />
+ <description>
+ Creates a [Transform3D] from an [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrPosef.html]XrPosef[/url].
+ </description>
+ </method>
+ <method name="xr_result">
+ <return type="bool" />
+ <param index="0" name="result" type="int" />
+ <param index="1" name="format" type="String" />
+ <param index="2" name="args" type="Array" />
+ <description>
+ Returns [code]true[/code] if the provided [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrResult.html]XrResult[/url] (cast to an integer) is successful. Otherwise returns [code]false[/code] and prints the [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrResult.html]XrResult[/url] converted to a string, with the specified additional information.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/OpenXRExtensionWrapperExtension.xml b/doc/classes/OpenXRExtensionWrapperExtension.xml
new file mode 100644
index 0000000000..004d10e588
--- /dev/null
+++ b/doc/classes/OpenXRExtensionWrapperExtension.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="OpenXRExtensionWrapperExtension" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Allows clients to implement OpenXR extensions with GDExtension.
+ </brief_description>
+ <description>
+ [OpenXRExtensionWrapperExtension] allows clients to implement OpenXR extensions with GDExtension. The extension should be registered with [method register_extension_wrapper].
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_get_requested_extensions" qualifiers="virtual">
+ <return type="Dictionary" />
+ <description>
+ Returns a [Dictionary] of OpenXR extensions related to this extension. The [Dictionary] should contain the name of the extension, mapped to a [code]bool *[/code] cast to an integer:
+ - If the [code]bool *[/code] is a [code]nullptr[/code] this extension is mandatory.
+ - If the [code]bool *[/code] points to a boolean, the boolean will be updated to [code]true[/code] if the extension is enabled.
+ </description>
+ </method>
+ <method name="_on_before_instance_created" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called before the OpenXR instance is created.
+ </description>
+ </method>
+ <method name="_on_event_polled" qualifiers="virtual">
+ <return type="bool" />
+ <param index="0" name="event" type="const void*" />
+ <description>
+ Called when there is an OpenXR event to process. When implementing, return [code]true[/code] if the event was handled, return [code]false[/code] otherwise.
+ </description>
+ </method>
+ <method name="_on_instance_created" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="instance" type="int" />
+ <description>
+ Called right after the OpenXR instance is created.
+ </description>
+ </method>
+ <method name="_on_instance_destroyed" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called right before the OpenXR instance is destroyed.
+ </description>
+ </method>
+ <method name="_on_pre_render" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called right before the XR viewports begin their rendering step.
+ </description>
+ </method>
+ <method name="_on_process" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called as part of the OpenXR process handling. This happens right before general and physics processing steps of the main loop. During this step controller data is queried and made available to game logic.
+ </description>
+ </method>
+ <method name="_on_register_metadata" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Allows extensions to register additional controller metadata. This function is called even when the OpenXR API is not constructed as the metadata needs to be available to the editor.
+ Extensions should also provide metadata regardless of whether they are supported on the host system. The controller data is used to setup action maps for users who may have access to the relevant hardware.
+ </description>
+ </method>
+ <method name="_on_session_created" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="session" type="int" />
+ <description>
+ Called right after the OpenXR session is created.
+ </description>
+ </method>
+ <method name="_on_session_destroyed" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called right before the OpenXR session is destroyed.
+ </description>
+ </method>
+ <method name="_on_state_exiting" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called when the OpenXR session state is changed to exiting.
+ </description>
+ </method>
+ <method name="_on_state_focused" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called when the OpenXR session state is changed to focused. This state is the active state when the game runs.
+ </description>
+ </method>
+ <method name="_on_state_idle" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called when the OpenXR session state is changed to idle.
+ </description>
+ </method>
+ <method name="_on_state_loss_pending" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called when the OpenXR session state is changed to loss pending.
+ </description>
+ </method>
+ <method name="_on_state_ready" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called when the OpenXR session state is changed to ready. This means OpenXR is ready to set up the session.
+ </description>
+ </method>
+ <method name="_on_state_stopping" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called when the OpenXR session state is changed to stopping.
+ </description>
+ </method>
+ <method name="_on_state_synchronized" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called when the OpenXR session state is changed to synchronized. OpenXR also returns to this state when the application loses focus.
+ </description>
+ </method>
+ <method name="_on_state_visible" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called when the OpenXR session state is changed to visible. This means OpenXR is now ready to receive frames.
+ </description>
+ </method>
+ <method name="_set_instance_create_info_and_get_next_pointer" qualifiers="virtual">
+ <return type="int" />
+ <param index="0" name="next_pointer" type="void*" />
+ <description>
+ Adds additional data structures when the OpenXR instance is created.
+ </description>
+ </method>
+ <method name="_set_session_create_and_get_next_pointer" qualifiers="virtual">
+ <return type="int" />
+ <param index="0" name="next_pointer" type="void*" />
+ <description>
+ Adds additional data structures when the OpenXR session is created.
+ </description>
+ </method>
+ <method name="_set_swapchain_create_info_and_get_next_pointer" qualifiers="virtual">
+ <return type="int" />
+ <param index="0" name="next_pointer" type="void*" />
+ <description>
+ Adds additional data structures when creating OpenXR swapchains.
+ </description>
+ </method>
+ <method name="_set_system_properties_and_get_next_pointer" qualifiers="virtual">
+ <return type="int" />
+ <param index="0" name="next_pointer" type="void*" />
+ <description>
+ Adds additional data structures when interogating OpenXR system abilities.
+ </description>
+ </method>
+ <method name="get_openxr_api">
+ <return type="OpenXRAPIExtension" />
+ <description>
+ Returns the created [OpenXRAPIExtension], which can be used to access the OpenXR API.
+ </description>
+ </method>
+ <method name="register_extension_wrapper">
+ <return type="void" />
+ <description>
+ Registers the extension. This should happen at core module initialization level.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml
index 51f353c17a..60d3a55711 100644
--- a/doc/classes/PackedByteArray.xml
+++ b/doc/classes/PackedByteArray.xml
@@ -346,7 +346,7 @@
<param index="0" name="byte_offset" type="int" />
<param index="1" name="allow_objects" type="bool" default="false" />
<description>
- Returns [code]true[/code] if a valid [Variant] value can be decoded at the [param byte_offset]. Returns [code]false[/code] othewrise or when the value is [Object]-derived and [param allow_objects] is [code]false[/code].
+ Returns [code]true[/code] if a valid [Variant] value can be decoded at the [param byte_offset]. Returns [code]false[/code] otherwise or when the value is [Object]-derived and [param allow_objects] is [code]false[/code].
</description>
</method>
<method name="hex_encode" qualifiers="const">
diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml
index a21be3f668..f3441f45ed 100644
--- a/doc/classes/PhysicsServer3D.xml
+++ b/doc/classes/PhysicsServer3D.xml
@@ -4,7 +4,7 @@
A server interface for low-level 3D physics access.
</brief_description>
<description>
- PhysicsServer2D is the server responsible for all 2D physics. It can directly create and manipulate all physics objects:
+ PhysicsServer3D is the server responsible for all 3D physics. It can directly create and manipulate all physics objects:
- A [i]space[/i] is a self-contained world for a physics simulation. It contains bodies, areas, and joints. Its state can be queried for collision and intersection information, and several parameters of the simulation can be modified.
- A [i]shape[/i] is a geometric shape such as a sphere, a box, a cylinder, or a polygon. It can be used for collision detection by adding it to a body/area, possibly with an extra transformation relative to the body/area's origin. Bodies/areas can have multiple (transformed) shapes added to them, and a single shape can be added to bodies/areas multiple times with different local transformations.
- A [i]body[/i] is a physical object which can be in static, kinematic, or rigid mode. Its state (such as position and velocity) can be queried and updated. A force integration callback can be set to customize the body's physics.
@@ -1146,7 +1146,7 @@
A factor applied to the movement across the slider axis once the limits get surpassed. The lower, the slower the movement.
</constant>
<constant name="SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION" value="3" enum="SliderJointParam">
- The amount of restitution once the limits are surpassed. The lower, the more velocityenergy gets lost.
+ The amount of restitution once the limits are surpassed. The lower, the more velocity-energy gets lost.
</constant>
<constant name="SLIDER_JOINT_LINEAR_LIMIT_DAMPING" value="4" enum="SliderJointParam">
The amount of damping once the slider limits are surpassed.
diff --git a/doc/classes/PinJoint3D.xml b/doc/classes/PinJoint3D.xml
index 3394a1d5fb..9009f0b658 100644
--- a/doc/classes/PinJoint3D.xml
+++ b/doc/classes/PinJoint3D.xml
@@ -4,7 +4,7 @@
A physics joint that attaches two 3D physics bodies at a single point, allowing them to freely rotate.
</brief_description>
<description>
- A physics joint that attaches two 2D physics bodies at a single point, allowing them to freely rotate. For example, a [RigidBody3D] can be attached to a [StaticBody3D] to create a pendulum or a seesaw.
+ A physics joint that attaches two 3D physics bodies at a single point, allowing them to freely rotate. For example, a [RigidBody3D] can be attached to a [StaticBody3D] to create a pendulum or a seesaw.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 744c72af4d..d240b6ef48 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -430,6 +430,12 @@
<member name="debug/gdscript/warnings/confusable_identifier" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier contains characters that can be confused with something else, like when mixing different alphabets.
</member>
+ <member name="debug/gdscript/warnings/confusable_local_declaration" type="int" setter="" getter="" default="1">
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier declared in the nested block has the same name as an identifier declared below in the parent block.
+ </member>
+ <member name="debug/gdscript/warnings/confusable_local_usage" type="int" setter="" getter="" default="1">
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier that will be shadowed below in the block is used.
+ </member>
<member name="debug/gdscript/warnings/constant_used_as_function" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a constant is used as a function.
</member>
@@ -819,7 +825,7 @@
Name of the .NET assembly. This name is used as the name of the [code].csproj[/code] and [code].sln[/code] files. By default, it's set to the name of the project ([member application/config/name]) allowing to change it in the future without affecting the .NET assembly.
</member>
<member name="dotnet/project/assembly_reload_attempts" type="int" setter="" getter="" default="3">
- Number of times to attempt assembly reloading after rebuilding .NET assembies. Effectively also the timeout in seconds to wait for unloading of script assemblies to finish.
+ Number of times to attempt assembly reloading after rebuilding .NET assemblies. Effectively also the timeout in seconds to wait for unloading of script assemblies to finish.
</member>
<member name="dotnet/project/solution_directory" type="String" setter="" getter="" default="&quot;&quot;">
Directory that contains the [code].sln[/code] file. By default, the [code].sln[/code] files is in the root of the project directory, next to the [code]project.godot[/code] and [code].csproj[/code] files.
@@ -935,23 +941,26 @@
Path to a custom [Font] resource to use as default for all GUI elements of the project.
</member>
<member name="gui/theme/default_font_antialiasing" type="int" setter="" getter="" default="1">
- Font anti-aliasing mode. See [member FontFile.antialiasing].
+ Font anti-aliasing mode for the default project font. See [member FontFile.antialiasing].
+ [b]Note:[/b] This setting does not affect custom [Font]s used within the project. Use the [b]Import[/b] dock for that instead (see [member ResourceImporterDynamicFont.antialiasing]).
</member>
<member name="gui/theme/default_font_generate_mipmaps" type="bool" setter="" getter="" default="false">
If set to [code]true[/code], the default font will have mipmaps generated. This prevents text from looking grainy when a [Control] is scaled down, or when a [Label3D] is viewed from a long distance (if [member Label3D.texture_filter] is set to a mode that displays mipmaps).
Enabling [member gui/theme/default_font_generate_mipmaps] increases font generation time and memory usage. Only enable this setting if you actually need it.
- [b]Note:[/b] This setting does not affect custom [Font]s used within the project.
+ [b]Note:[/b] This setting does not affect custom [Font]s used within the project. Use the [b]Import[/b] dock for that instead (see [member ResourceImporterDynamicFont.generate_mipmaps]).
</member>
<member name="gui/theme/default_font_hinting" type="int" setter="" getter="" default="1">
- Default font hinting mode. See [member FontFile.hinting].
+ Font hinting mode for the default project font. See [member FontFile.hinting].
+ [b]Note:[/b] This setting does not affect custom [Font]s used within the project. Use the [b]Import[/b] dock for that instead (see [member ResourceImporterDynamicFont.hinting]).
</member>
<member name="gui/theme/default_font_multichannel_signed_distance_field" type="bool" setter="" getter="" default="false">
If set to [code]true[/code], the default font will use multichannel signed distance field (MSDF) for crisp rendering at any size. Since this approach does not rely on rasterizing the font every time its size changes, this allows for resizing the font in real-time without any performance penalty. Text will also not look grainy for [Control]s that are scaled down (or for [Label3D]s viewed from a long distance).
MSDF font rendering can be combined with [member gui/theme/default_font_generate_mipmaps] to further improve font rendering quality when scaled down.
- [b]Note:[/b] This setting does not affect custom [Font]s used within the project.
+ [b]Note:[/b] This setting does not affect custom [Font]s used within the project. Use the [b]Import[/b] dock for that instead (see [member ResourceImporterDynamicFont.multichannel_signed_distance_field]).
</member>
<member name="gui/theme/default_font_subpixel_positioning" type="int" setter="" getter="" default="1">
- Default font glyph subpixel positioning mode. See [member FontFile.subpixel_positioning].
+ Font glyph subpixel positioning mode for the default project font. See [member FontFile.subpixel_positioning].
+ [b]Note:[/b] This setting does not affect custom [Font]s used within the project. Use the [b]Import[/b] dock for that instead (see [member ResourceImporterDynamicFont.subpixel_positioning]).
</member>
<member name="gui/theme/default_theme_scale" type="float" setter="" getter="" default="1.0">
The default scale factor for [Control]s, when not overridden by a [Theme].
@@ -1943,13 +1952,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">
@@ -1967,6 +1976,9 @@
<member name="navigation/3d/default_link_connection_radius" type="float" setter="" getter="" default="1.0">
Default link connection radius for 3D navigation maps. See [method NavigationServer3D.map_set_link_connection_radius].
</member>
+ <member name="navigation/3d/default_up" type="Vector3" setter="" getter="" default="Vector3(0, 1, 0)">
+ Default up orientation for 3D navigation maps. See [method NavigationServer3D.map_set_up].
+ </member>
<member name="navigation/3d/use_edge_connections" type="bool" setter="" getter="" default="true">
If enabled 3D navigation regions will use edge connections to connect with other navigation regions within proximity of the navigation map edge connection margin. This setting only affects World3D default navigation maps.
</member>
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/Rect2.xml b/doc/classes/Rect2.xml
index 1b6e2bbeb2..fdabb110c6 100644
--- a/doc/classes/Rect2.xml
+++ b/doc/classes/Rect2.xml
@@ -4,10 +4,10 @@
A 2D axis-aligned bounding box using floating-point coordinates.
</brief_description>
<description>
- [Rect2] consists of a position, a size, and several utility functions. It is typically used for fast overlap tests.
- It uses floating-point coordinates. If you need integer coordinates, use [Rect2i] instead.
- The 3D counterpart to [Rect2] is [AABB].
- Negative values for [member size] are not supported and will not work for most methods. Use [method abs] to get a Rect2 with a positive size.
+ The [Rect2] built-in [Variant] type represents an axis-aligned rectangle in a 2D space. It is defined by its [member position] and [member size], which are [Vector2]. It is frequently used for fast overlap tests (see [method intersects]). Although [Rect2] itself is axis-aligned, it can be combined with [Transform2D] to represent a rotated or skewed rectangle.
+ For integer coordinates, use [Rect2i]. The 3D equivalent to [Rect2] is [AABB].
+ [b]Note:[/b] Negative values for [member size] are not supported. With negative size, most [Rect2] methods do not work correctly. Use [method abs] to get an equivalent [Rect2] with a non-negative size.
+ [b]Note:[/b] In a boolean context, a [Rect2] evaluates to [code]false[/code] if both [member position] and [member size] are zero (equal to [constant Vector2.ZERO]). Otherwise, it always evaluates to [code]true[/code].
</description>
<tutorials>
<link title="Math documentation index">$DOCS_URL/tutorials/math/index.html</link>
@@ -18,7 +18,7 @@
<constructor name="Rect2">
<return type="Rect2" />
<description>
- Constructs a default-initialized [Rect2] with default (zero) values of [member position] and [member size].
+ Constructs a [Rect2] with its [member position] and [member size] set to [constant Vector2.ZERO].
</description>
</constructor>
<constructor name="Rect2">
@@ -40,7 +40,7 @@
<param index="0" name="position" type="Vector2" />
<param index="1" name="size" type="Vector2" />
<description>
- Constructs a [Rect2] by position and size.
+ Constructs a [Rect2] by [param position] and [param size].
</description>
</constructor>
<constructor name="Rect2">
@@ -50,7 +50,7 @@
<param index="2" name="width" type="float" />
<param index="3" name="height" type="float" />
<description>
- Constructs a [Rect2] by x, y, width, and height.
+ Constructs a [Rect2] by setting its [member position] to ([param x], [param y]), and its [member size] to ([param width], [param height]).
</description>
</constructor>
</constructors>
@@ -58,34 +58,44 @@
<method name="abs" qualifiers="const">
<return type="Rect2" />
<description>
- Returns a [Rect2] with equivalent position and area, modified so that the top-left corner is the origin and [code]width[/code] and [code]height[/code] are positive.
+ Returns a [Rect2] equivalent to this rectangle, with its width and height modified to be non-negative values, and with its [member position] being the top-left corner of the rectangle.
+ [codeblocks]
+ [gdscript]
+ var rect = Rect2(25, 25, -100, -50)
+ var absolute = rect.abs() # absolute is Rect2(-75, -25, 100, 50)
+ [/gdscript]
+ [csharp]
+ var rect = new Rect2(25, 25, -100, -50);
+ var absolute = rect.Abs(); // absolute is Rect2(-75, -25, 100, 50)
+ [/csharp]
+ [/codeblocks]
+ [b]Note:[/b] It's recommended to use this method when [member size] is negative, as most other methods in Godot assume that the [member position] is the top-left corner, and the [member end] is the bottom-right corner.
</description>
</method>
<method name="encloses" qualifiers="const">
<return type="bool" />
<param index="0" name="b" type="Rect2" />
<description>
- Returns [code]true[/code] if this [Rect2] completely encloses another one.
+ Returns [code]true[/code] if this rectangle [i]completely[/i] encloses the [param b] rectangle.
</description>
</method>
<method name="expand" qualifiers="const">
<return type="Rect2" />
<param index="0" name="to" type="Vector2" />
<description>
- Returns a copy of this [Rect2] expanded to include a given point.
- [b]Example:[/b]
+ Returns a copy of this rectangle expanded to include the given [param to] point, if necessary.
[codeblocks]
[gdscript]
- # position (-3, 2), size (1, 1)
- var rect = Rect2(Vector2(-3, 2), Vector2(1, 1))
- # position (-3, -1), size (3, 4), so we fit both rect and Vector2(0, -1)
- var rect2 = rect.expand(Vector2(0, -1))
+ var rect = Rect2(0, 0, 5, 2)
+
+ rect = rect.expand(Vector2(10, 0)) # rect is Rect2(0, 0, 10, 2)
+ rect = rect.expand(Vector2(-5, 5)) # rect is Rect2(-5, 0, 10, 5)
[/gdscript]
[csharp]
- // position (-3, 2), size (1, 1)
- var rect = new Rect2(new Vector2(-3, 2), new Vector2(1, 1));
- // position (-3, -1), size (3, 4), so we fit both rect and Vector2(0, -1)
- var rect2 = rect.Expand(new Vector2(0, -1));
+ var rect = new Rect2(0, 0, 5, 2);
+
+ rect = rect.Expand(new Vector2(10, 0)); // rect is Rect2(0, 0, 10, 2)
+ rect = rect.Expand(new Vector2(-5, 5)); // rect is Rect2(-5, 0, 10, 5)
[/csharp]
[/codeblocks]
</description>
@@ -93,20 +103,30 @@
<method name="get_area" qualifiers="const">
<return type="float" />
<description>
- Returns the area of the [Rect2]. See also [method has_area].
+ Returns the rectangle's area. This is equivalent to [code]size.x * size.y[/code]. See also [method has_area].
</description>
</method>
<method name="get_center" qualifiers="const">
<return type="Vector2" />
<description>
- Returns the center of the [Rect2], which is equal to [member position] + ([member size] / 2).
+ Returns the center point of the rectangle. This is the same as [code]position + (size / 2.0)[/code].
</description>
</method>
<method name="grow" qualifiers="const">
<return type="Rect2" />
<param index="0" name="amount" type="float" />
<description>
- Returns a copy of the [Rect2] grown by the specified [param amount] on all sides.
+ Returns a copy of this rectangle extended on all sides by the given [param amount]. A negative [param amount] shrinks the rectangle instead. See also [method grow_individual] and [method grow_side].
+ [codeblocks]
+ [gdscript]
+ var a = Rect2(4, 4, 8, 8).grow(4) # a is Rect2(0, 0, 16, 16)
+ var b = Rect2(0, 0, 8, 4).grow(2) # b is Rect2(-2, -2, 12, 8)
+ [/gdscript]
+ [csharp]
+ var a = new Rect2(4, 4, 8, 8).Grow(4); // a is Rect2(0, 0, 16, 16)
+ var b = new Rect2(0, 0, 8, 4).Grow(2); // b is Rect2(-2, -2, 12, 8)
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="grow_individual" qualifiers="const">
@@ -116,7 +136,7 @@
<param index="2" name="right" type="float" />
<param index="3" name="bottom" type="float" />
<description>
- Returns a copy of the [Rect2] grown by the specified amount on each side individually.
+ Returns a copy of this rectangle with its [param left], [param top], [param right], and [param bottom] sides extended by the given amounts. Negative values shrink the sides, instead. See also [method grow] and [method grow_side].
</description>
</method>
<method name="grow_side" qualifiers="const">
@@ -124,29 +144,43 @@
<param index="0" name="side" type="int" />
<param index="1" name="amount" type="float" />
<description>
- Returns a copy of the [Rect2] grown by the specified [param amount] on the specified [enum Side].
+ Returns a copy of this rectangle with its [param side] extended by the given [param amount] (see [enum Side] constants). A negative [param amount] shrinks the rectangle, instead. See also [method grow] and [method grow_individual].
</description>
</method>
<method name="has_area" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if the [Rect2] has area, and [code]false[/code] if the [Rect2] is linear, empty, or has a negative [member size]. See also [method get_area].
+ Returns [code]true[/code] if this rectangle has positive width and height. See also [method get_area].
</description>
</method>
<method name="has_point" qualifiers="const">
<return type="bool" />
<param index="0" name="point" type="Vector2" />
<description>
- Returns [code]true[/code] if the [Rect2] contains a point. By convention, the right and bottom edges of the [Rect2] are considered exclusive, so points on these edges are [b]not[/b] included.
- [b]Note:[/b] This method is not reliable for [Rect2] with a [i]negative size[/i]. Use [method abs] to get a positive sized equivalent rectangle to check for contained points.
+ Returns [code]true[/code] if the rectangle contains the given [param point]. By convention, points on the right and bottom edges are [b]not[/b] included.
+ [b]Note:[/b] This method is not reliable for [Rect2] with a [i]negative[/i] [member size]. Use [method abs] first to get a valid rectangle.
</description>
</method>
<method name="intersection" qualifiers="const">
<return type="Rect2" />
<param index="0" name="b" type="Rect2" />
<description>
- Returns the intersection of this [Rect2] and [param b].
- If the rectangles do not intersect, an empty [Rect2] is returned.
+ Returns the intersection between this rectangle and [param b]. If the rectangles do not intersect, returns an empty [Rect2].
+ [codeblocks]
+ [gdscript]
+ var rect1 = Rect2(0, 0, 5, 10)
+ var rect2 = Rect2(2, 0, 8, 4)
+
+ var a = rect1.intersection(rect2) # a is Rect2(2, 0, 3, 4)
+ [/gdscript]
+ [csharp]
+ var rect1 = new Rect2(0, 0, 5, 10);
+ var rect2 = new Rect2(2, 0, 8, 4);
+
+ var a = rect1.Intersection(rect2); // a is Rect2(2, 0, 3, 4)
+ [/csharp]
+ [/codeblocks]
+ [b]Note:[/b] If you only need to know whether two rectangles are overlapping, use [method intersects], instead.
</description>
</method>
<method name="intersects" qualifiers="const">
@@ -154,41 +188,40 @@
<param index="0" name="b" type="Rect2" />
<param index="1" name="include_borders" type="bool" default="false" />
<description>
- Returns [code]true[/code] if the [Rect2] overlaps with [param b] (i.e. they have at least one point in common).
- If [param include_borders] is [code]true[/code], they will also be considered overlapping if their borders touch, even without intersection.
+ Returns [code]true[/code] if this rectangle overlaps with the [param b] rectangle. The edges of both rectangles are excluded, unless [param include_borders] is [code]true[/code].
</description>
</method>
<method name="is_equal_approx" qualifiers="const">
<return type="bool" />
<param index="0" name="rect" type="Rect2" />
<description>
- Returns [code]true[/code] if this [Rect2] and [param rect] are approximately equal, by calling [code]is_equal_approx[/code] on each component.
+ Returns [code]true[/code] if this rectangle and [param rect] are approximately equal, by calling [method Vector2.is_equal_approx] on the [member position] and the [member size].
</description>
</method>
<method name="is_finite" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if this [Rect2] is finite, by calling [method @GlobalScope.is_finite] on each component.
+ Returns [code]true[/code] if this rectangle's values are finite, by calling [method Vector2.is_finite] on the [member position] and the [member size].
</description>
</method>
<method name="merge" qualifiers="const">
<return type="Rect2" />
<param index="0" name="b" type="Rect2" />
<description>
- Returns a larger [Rect2] that contains this [Rect2] and [param b].
+ Returns a [Rect2] that encloses both this rectangle and [param b] around the edges. See also [method encloses].
</description>
</method>
</methods>
<members>
<member name="end" type="Vector2" setter="" getter="" default="Vector2(0, 0)">
- Ending corner. This is calculated as [code]position + size[/code]. Setting this value will change the size.
+ The ending point. This is usually the bottom-right corner of the rectangle, and is equivalent to [code]position + size[/code]. Setting this point affects the [member size].
</member>
<member name="position" type="Vector2" setter="" getter="" default="Vector2(0, 0)">
- Beginning corner. Typically has values lower than [member end].
+ The origin point. This is usually the top-left corner of the rectangle.
</member>
<member name="size" type="Vector2" setter="" getter="" default="Vector2(0, 0)">
- Size from [member position] to [member end]. Typically, all components are positive.
- If the size is negative, you can use [method abs] to fix it.
+ The rectangle's width and height, starting from [member position]. Setting this value also affects the [member end] point.
+ [b]Note:[/b] It's recommended setting the width and height to non-negative values, as most methods in Godot assume that the [member position] is the top-left corner, and the [member end] is the bottom-right corner. To get an equivalent rectangle with non-negative size, use [method abs].
</member>
</members>
<operators>
@@ -196,7 +229,7 @@
<return type="bool" />
<param index="0" name="right" type="Rect2" />
<description>
- Returns [code]true[/code] if the rectangles are not equal.
+ Returns [code]true[/code] if the [member position] or [member size] of both rectangles are not equal.
[b]Note:[/b] Due to floating-point precision errors, consider using [method is_equal_approx] instead, which is more reliable.
</description>
</operator>
@@ -211,7 +244,7 @@
<return type="bool" />
<param index="0" name="right" type="Rect2" />
<description>
- Returns [code]true[/code] if the rectangles are exactly equal.
+ Returns [code]true[/code] if both [member position] and [member size] of the rectangles are exactly equal, respectively.
[b]Note:[/b] Due to floating-point precision errors, consider using [method is_equal_approx] instead, which is more reliable.
</description>
</operator>
diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml
index 5bb1a83cdc..79d9e3f6df 100644
--- a/doc/classes/Rect2i.xml
+++ b/doc/classes/Rect2i.xml
@@ -4,9 +4,10 @@
A 2D axis-aligned bounding box using integer coordinates.
</brief_description>
<description>
- [Rect2i] consists of a position, a size, and several utility functions. It is typically used for fast overlap tests.
- It uses integer coordinates. If you need floating-point coordinates, use [Rect2] instead.
- Negative values for [member size] are not supported and will not work for most methods. Use [method abs] to get a Rect2i with a positive size.
+ The [Rect2i] built-in [Variant] type represents an axis-aligned rectangle in a 2D space, using integer coordinates. It is defined by its [member position] and [member size], which are [Vector2i]. Because it does not rotate, it is frequently used for fast overlap tests (see [method intersects]).
+ For floating-point coordinates, see [Rect2].
+ [b]Note:[/b] Negative values for [member size] are not supported. With negative size, most [Rect2i] methods do not work correctly. Use [method abs] to get an equivalent [Rect2i] with a non-negative size.
+ [b]Note:[/b] In a boolean context, a [Rect2i] evaluates to [code]false[/code] if both [member position] and [member size] are zero (equal to [constant Vector2i.ZERO]). Otherwise, it always evaluates to [code]true[/code].
</description>
<tutorials>
<link title="Math documentation index">$DOCS_URL/tutorials/math/index.html</link>
@@ -16,7 +17,7 @@
<constructor name="Rect2i">
<return type="Rect2i" />
<description>
- Constructs a default-initialized [Rect2i] with default (zero) values of [member position] and [member size].
+ Constructs a [Rect2i] with its [member position] and [member size] set to [constant Vector2i.ZERO].
</description>
</constructor>
<constructor name="Rect2i">
@@ -30,7 +31,7 @@
<return type="Rect2i" />
<param index="0" name="from" type="Rect2" />
<description>
- Constructs a new [Rect2i] from [Rect2]. The floating point coordinates will be truncated.
+ Constructs a [Rect2i] from a [Rect2]. The floating-point coordinates are truncated.
</description>
</constructor>
<constructor name="Rect2i">
@@ -38,7 +39,7 @@
<param index="0" name="position" type="Vector2i" />
<param index="1" name="size" type="Vector2i" />
<description>
- Constructs a [Rect2i] by position and size.
+ Constructs a [Rect2i] by [param position] and [param size].
</description>
</constructor>
<constructor name="Rect2i">
@@ -48,7 +49,7 @@
<param index="2" name="width" type="int" />
<param index="3" name="height" type="int" />
<description>
- Constructs a [Rect2i] by x, y, width, and height.
+ Constructs a [Rect2i] by setting its [member position] to ([param x], [param y]), and its [member size] to ([param width], [param height]).
</description>
</constructor>
</constructors>
@@ -56,7 +57,18 @@
<method name="abs" qualifiers="const">
<return type="Rect2i" />
<description>
- Returns a [Rect2i] with equivalent position and area, modified so that the top-left corner is the origin and [code]width[/code] and [code]height[/code] are positive.
+ Returns a [Rect2i] equivalent to this rectangle, with its width and height modified to be non-negative values, and with its [member position] being the top-left corner of the rectangle.
+ [codeblocks]
+ [gdscript]
+ var rect = Rect2i(25, 25, -100, -50)
+ var absolute = rect.abs() # absolute is Rect2i(-75, -25, 100, 50)
+ [/gdscript]
+ [csharp]
+ var rect = new Rect2I(25, 25, -100, -50);
+ var absolute = rect.Abs(); // absolute is Rect2I(-75, -25, 100, 50)
+ [/csharp]
+ [/codeblocks]
+ [b]Note:[/b] It's recommended to use this method when [member size] is negative, as most other methods in Godot assume that the [member position] is the top-left corner, and the [member end] is the bottom-right corner.
</description>
</method>
<method name="encloses" qualifiers="const">
@@ -70,19 +82,19 @@
<return type="Rect2i" />
<param index="0" name="to" type="Vector2i" />
<description>
- Returns a copy of this [Rect2i] expanded so that the borders align with the given point.
+ Returns a copy of this rectangle expanded to include the given [param to] point, if necessary.
[codeblocks]
[gdscript]
- # position (-3, 2), size (1, 1)
- var rect = Rect2i(Vector2i(-3, 2), Vector2i(1, 1))
- # position (-3, -1), size (3, 4), so we fit both rect and Vector2i(0, -1)
- var rect2 = rect.expand(Vector2i(0, -1))
+ var rect = Rect2i(0, 0, 5, 2)
+
+ rect = rect.expand(Vector2i(10, 0)) # rect is Rect2i(0, 0, 10, 2)
+ rect = rect.expand(Vector2i(-5, 5)) # rect is Rect2i(-5, 0, 10, 5)
[/gdscript]
[csharp]
- // position (-3, 2), size (1, 1)
- var rect = new Rect2I(new Vector2I(-3, 2), new Vector2I(1, 1));
- // position (-3, -1), size (3, 4), so we fit both rect and Vector2I(0, -1)
- var rect2 = rect.Expand(new Vector2I(0, -1));
+ var rect = new Rect2I(0, 0, 5, 2);
+
+ rect = rect.Expand(new Vector2I(10, 0)); // rect is Rect2I(0, 0, 10, 2)
+ rect = rect.Expand(new Vector2I(-5, 5)); // rect is Rect2I(-5, 0, 10, 5)
[/csharp]
[/codeblocks]
</description>
@@ -90,21 +102,31 @@
<method name="get_area" qualifiers="const">
<return type="int" />
<description>
- Returns the area of the [Rect2i]. See also [method has_area].
+ Returns the rectangle's area. This is equivalent to [code]size.x * size.y[/code]. See also [method has_area].
</description>
</method>
<method name="get_center" qualifiers="const">
<return type="Vector2i" />
<description>
- Returns the center of the [Rect2i], which is equal to [member position] + ([member size] / 2).
- If [member size] is an odd number, the returned center value will be rounded towards [member position].
+ Returns the center point of the rectangle. This is the same as [code]position + (size / 2)[/code].
+ [b]Note:[/b] If the [member size] is odd, the result will be rounded towards [member position].
</description>
</method>
<method name="grow" qualifiers="const">
<return type="Rect2i" />
<param index="0" name="amount" type="int" />
<description>
- Returns a copy of the [Rect2i] grown by the specified [param amount] on all sides.
+ Returns a copy of this rectangle extended on all sides by the given [param amount]. A negative [param amount] shrinks the rectangle instead. See also [method grow_individual] and [method grow_side].
+ [codeblocks]
+ [gdscript]
+ var a = Rect2i(4, 4, 8, 8).grow(4) # a is Rect2i(0, 0, 16, 16)
+ var b = Rect2i(0, 0, 8, 4).grow(2) # b is Rect2i(-2, -2, 12, 8)
+ [/gdscript]
+ [csharp]
+ var a = new Rect2I(4, 4, 8, 8).Grow(4); // a is Rect2I(0, 0, 16, 16)
+ var b = new Rect2I(0, 0, 8, 4).Grow(2); // b is Rect2I(-2, -2, 12, 8)
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="grow_individual" qualifiers="const">
@@ -114,7 +136,7 @@
<param index="2" name="right" type="int" />
<param index="3" name="bottom" type="int" />
<description>
- Returns a copy of the [Rect2i] grown by the specified amount on each side individually.
+ Returns a copy of this rectangle with its [param left], [param top], [param right], and [param bottom] sides extended by the given amounts. Negative values shrink the sides, instead. See also [method grow] and [method grow_side].
</description>
</method>
<method name="grow_side" qualifiers="const">
@@ -122,56 +144,70 @@
<param index="0" name="side" type="int" />
<param index="1" name="amount" type="int" />
<description>
- Returns a copy of the [Rect2i] grown by the specified [param amount] on the specified [enum Side].
+ Returns a copy of this rectangle with its [param side] extended by the given [param amount] (see [enum Side] constants). A negative [param amount] shrinks the rectangle, instead. See also [method grow] and [method grow_individual].
</description>
</method>
<method name="has_area" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if the [Rect2i] has area, and [code]false[/code] if the [Rect2i] is linear, empty, or has a negative [member size]. See also [method get_area].
+ Returns [code]true[/code] if this rectangle has positive width and height. See also [method get_area].
</description>
</method>
<method name="has_point" qualifiers="const">
<return type="bool" />
<param index="0" name="point" type="Vector2i" />
<description>
- Returns [code]true[/code] if the [Rect2i] contains a point. By convention, the right and bottom edges of the [Rect2i] are considered exclusive, so points on these edges are [b]not[/b] included.
- [b]Note:[/b] This method is not reliable for [Rect2i] with a [i]negative size[/i]. Use [method abs] to get a positive sized equivalent rectangle to check for contained points.
+ Returns [code]true[/code] if the rectangle contains the given [param point]. By convention, points on the right and bottom edges are [b]not[/b] included.
+ [b]Note:[/b] This method is not reliable for [Rect2i] with a [i]negative[/i] [member size]. Use [method abs] first to get a valid rectangle.
</description>
</method>
<method name="intersection" qualifiers="const">
<return type="Rect2i" />
<param index="0" name="b" type="Rect2i" />
<description>
- Returns the intersection of this [Rect2i] and [param b].
- If the rectangles do not intersect, an empty [Rect2i] is returned.
+ Returns the intersection between this rectangle and [param b]. If the rectangles do not intersect, returns an empty [Rect2i].
+ [codeblocks]
+ [gdscript]
+ var a = Rect2i(0, 0, 5, 10)
+ var b = Rect2i(2, 0, 8, 4)
+
+ var c = a.intersection(b) # c is Rect2i(2, 0, 3, 4)
+ [/gdscript]
+ [csharp]
+ var a = new Rect2I(0, 0, 5, 10);
+ var b = new Rect2I(2, 0, 8, 4);
+
+ var c = rect1.Intersection(rect2); // c is Rect2I(2, 0, 3, 4)
+ [/csharp]
+ [/codeblocks]
+ [b]Note:[/b] If you only need to know whether two rectangles are overlapping, use [method intersects], instead.
</description>
</method>
<method name="intersects" qualifiers="const">
<return type="bool" />
<param index="0" name="b" type="Rect2i" />
<description>
- Returns [code]true[/code] if the [Rect2i] overlaps with [param b] (i.e. they have at least one point in common).
+ Returns [code]true[/code] if this rectangle overlaps with the [param b] rectangle. The edges of both rectangles are excluded.
</description>
</method>
<method name="merge" qualifiers="const">
<return type="Rect2i" />
<param index="0" name="b" type="Rect2i" />
<description>
- Returns a larger [Rect2i] that contains this [Rect2i] and [param b].
+ Returns a [Rect2i] that encloses both this rectangle and [param b] around the edges. See also [method encloses].
</description>
</method>
</methods>
<members>
<member name="end" type="Vector2i" setter="" getter="" default="Vector2i(0, 0)">
- Ending corner. This is calculated as [code]position + size[/code]. Setting this value will change the size.
+ The ending point. This is usually the bottom-right corner of the rectangle, and is equivalent to [code]position + size[/code]. Setting this point affects the [member size].
</member>
<member name="position" type="Vector2i" setter="" getter="" default="Vector2i(0, 0)">
- Beginning corner. Typically has values lower than [member end].
+ The origin point. This is usually the top-left corner of the rectangle.
</member>
<member name="size" type="Vector2i" setter="" getter="" default="Vector2i(0, 0)">
- Size from [member position] to [member end]. Typically, all components are positive.
- If the size is negative, you can use [method abs] to fix it.
+ The rectangle's width and height, starting from [member position]. Setting this value also affects the [member end] point.
+ [b]Note:[/b] It's recommended setting the width and height to non-negative values, as most methods in Godot assume that the [member position] is the top-left corner, and the [member end] is the bottom-right corner. To get an equivalent rectangle with non-negative size, use [method abs].
</member>
</members>
<operators>
@@ -179,14 +215,14 @@
<return type="bool" />
<param index="0" name="right" type="Rect2i" />
<description>
- Returns [code]true[/code] if the rectangles are not equal.
+ Returns [code]true[/code] if the [member position] or [member size] of both rectangles are not equal.
</description>
</operator>
<operator name="operator ==">
<return type="bool" />
<param index="0" name="right" type="Rect2i" />
<description>
- Returns [code]true[/code] if the rectangles are equal.
+ Returns [code]true[/code] if both [member position] and [member size] of the rectangles are equal, respectively.
</description>
</operator>
</operators>
diff --git a/doc/classes/RenderSceneBuffers.xml b/doc/classes/RenderSceneBuffers.xml
new file mode 100644
index 0000000000..b2a5213dba
--- /dev/null
+++ b/doc/classes/RenderSceneBuffers.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="RenderSceneBuffers" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Abstract scene buffers object, created for each viewport for which 3D rendering is done.
+ </brief_description>
+ <description>
+ Abstract scene buffers object, created for each viewport for which 3D rendering is done. It manages any additional buffers used during rendering and will discard buffers when the viewport is resized.
+ [b]Note:[/b] this is an internal rendering server object only exposed for GDExtension plugins.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="configure">
+ <return type="void" />
+ <param index="0" name="config" type="RenderSceneBuffersConfiguration" />
+ <description>
+ This method is called by the rendering server when the associated viewports configuration is changed. It will discard the old buffers and recreate the internal buffers used.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/RenderSceneBuffersConfiguration.xml b/doc/classes/RenderSceneBuffersConfiguration.xml
new file mode 100644
index 0000000000..cb36955206
--- /dev/null
+++ b/doc/classes/RenderSceneBuffersConfiguration.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="RenderSceneBuffersConfiguration" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Configuration object used to setup a [RenderSceneBuffers] object.
+ </brief_description>
+ <description>
+ This configuration object is created and populated by the render engine on a viewport change and used to (re)configure a [RenderSceneBuffers] object.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="fsr_sharpness" type="float" setter="set_fsr_sharpness" getter="get_fsr_sharpness" default="0.0">
+ FSR Sharpness applicable if FSR upscaling is used.
+ </member>
+ <member name="internal_size" type="Vector2i" setter="set_internal_size" getter="get_internal_size" default="Vector2i(0, 0)">
+ The size of the 3D render buffer used for rendering.
+ </member>
+ <member name="msaa_3d" type="int" setter="set_msaa_3d" getter="get_msaa_3d" enum="RenderingServer.ViewportMSAA" default="0">
+ The MSAA mode we're using for 3D rendering.
+ </member>
+ <member name="render_target" type="RID" setter="set_render_target" getter="get_render_target" default="RID()">
+ The render target associated with these buffer.
+ </member>
+ <member name="scaling_3d_mode" type="int" setter="set_scaling_3d_mode" getter="get_scaling_3d_mode" enum="RenderingServer.ViewportScaling3DMode" default="255">
+ The requested scaling mode with which we upscale/downscale if [member internal_size] and [member target_size] are not equal.
+ </member>
+ <member name="screen_space_aa" type="int" setter="set_screen_space_aa" getter="get_screen_space_aa" enum="RenderingServer.ViewportScreenSpaceAA" default="0">
+ The requested screen space AA applied in post processing.
+ </member>
+ <member name="target_size" type="Vector2i" setter="set_target_size" getter="get_target_size" default="Vector2i(0, 0)">
+ The target (upscale) size if scaling is used.
+ </member>
+ <member name="texture_mipmap_bias" type="float" setter="set_texture_mipmap_bias" getter="get_texture_mipmap_bias" default="0.0">
+ Bias applied to mipmaps.
+ </member>
+ <member name="view_count" type="int" setter="set_view_count" getter="get_view_count" default="1">
+ The number of views we're rendering.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/RenderSceneBuffersExtension.xml b/doc/classes/RenderSceneBuffersExtension.xml
new file mode 100644
index 0000000000..f9960f0fbe
--- /dev/null
+++ b/doc/classes/RenderSceneBuffersExtension.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="RenderSceneBuffersExtension" inherits="RenderSceneBuffers" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ This class allows for a RenderSceneBuffer implementation to be made in GDExtension.
+ </brief_description>
+ <description>
+ This class allows for a RenderSceneBuffer implementation to be made in GDExtension.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_configure" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="config" type="RenderSceneBuffersConfiguration" />
+ <description>
+ Implement this in GDExtension to handle the (re)sizing of a viewport.
+ </description>
+ </method>
+ <method name="_set_fsr_sharpness" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="fsr_sharpness" type="float" />
+ <description>
+ Implement this in GDExtension to record a new FSR sharpness value.
+ </description>
+ </method>
+ <method name="_set_texture_mipmap_bias" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="texture_mipmap_bias" type="float" />
+ <description>
+ Implement this in GDExtension to change the texture mipmap bias.
+ </description>
+ </method>
+ <method name="_set_use_debanding" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="use_debanding" type="bool" />
+ <description>
+ Implement this in GDExtension to react to the debanding flag changing.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/RenderSceneBuffersRD.xml b/doc/classes/RenderSceneBuffersRD.xml
new file mode 100644
index 0000000000..3af5b78fcc
--- /dev/null
+++ b/doc/classes/RenderSceneBuffersRD.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="RenderSceneBuffersRD" inherits="RenderSceneBuffers" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Abstract render scene buffer implementation for the RenderingDevice based renderers.
+ </brief_description>
+ <description>
+ This object manages all 3D rendering buffers for the rendering device based renderers. An instance of this object is created for every viewport that has 3D rendering enabled.
+ All buffers are organised in [b]contexts[/b]. The default context is called [b]render_buffers[/b] and can contain amongst others the color buffer, depth buffer, velocity buffers, VRS density map and MSAA variants of these buffers.
+ Buffers are only guaranteed to exist during rendering of the viewport.
+ [b]Note:[/b] this is an internal rendering server object only exposed for GDExtension plugins.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="clear_context">
+ <return type="void" />
+ <param index="0" name="context" type="StringName" />
+ <description>
+ Frees all buffers related to this context.
+ </description>
+ </method>
+ <method name="create_texture">
+ <return type="RID" />
+ <param index="0" name="context" type="StringName" />
+ <param index="1" name="name" type="StringName" />
+ <param index="2" name="data_format" type="int" enum="RenderingDevice.DataFormat" />
+ <param index="3" name="usage_bits" type="int" />
+ <param index="4" name="texture_samples" type="int" enum="RenderingDevice.TextureSamples" />
+ <param index="5" name="size" type="Vector2i" />
+ <param index="6" name="layers" type="int" />
+ <param index="7" name="mipmaps" type="int" />
+ <param index="8" name="unique" type="bool" />
+ <description>
+ Create a new texture with the given definition and cache this under the given name. Will return the existing texture if it already exists.
+ </description>
+ </method>
+ <method name="create_texture_from_format">
+ <return type="RID" />
+ <param index="0" name="context" type="StringName" />
+ <param index="1" name="name" type="StringName" />
+ <param index="2" name="format" type="RDTextureFormat" />
+ <param index="3" name="view" type="RDTextureView" />
+ <param index="4" name="unique" type="bool" />
+ <description>
+ Create a new texture using the given format and view and cache this under the given name. Will return the existing texture if it already exists.
+ </description>
+ </method>
+ <method name="create_texture_view">
+ <return type="RID" />
+ <param index="0" name="context" type="StringName" />
+ <param index="1" name="name" type="StringName" />
+ <param index="2" name="view_name" type="StringName" />
+ <param index="3" name="view" type="RDTextureView" />
+ <description>
+ Create a new texture view for an existing texture and cache this under the given view_name. Will return the existing teture view if it already exists. Will error if the source texture doesn't exist.
+ </description>
+ </method>
+ <method name="get_color_layer">
+ <return type="RID" />
+ <param index="0" name="layer" type="int" />
+ <description>
+ Returns the specified layer from the color texture we are rendering 3D content to.
+ </description>
+ </method>
+ <method name="get_color_texture">
+ <return type="RID" />
+ <description>
+ Returns the color texture we are rendering 3D content to. If multiview is used this will be a texture array with all views.
+ </description>
+ </method>
+ <method name="get_depth_layer">
+ <return type="RID" />
+ <param index="0" name="layer" type="int" />
+ <description>
+ Returns the specified layer from the depth texture we are rendering 3D content to.
+ </description>
+ </method>
+ <method name="get_depth_texture">
+ <return type="RID" />
+ <description>
+ Returns the depth texture we are rendering 3D content to. If multiview is used this will be a texture array with all views.
+ </description>
+ </method>
+ <method name="get_internal_size" qualifiers="const">
+ <return type="Vector2i" />
+ <description>
+ Returns the internal size of the render buffer (size before upscaling) with which textures are created by default.
+ </description>
+ </method>
+ <method name="get_render_target" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the render target associated with this buffers object.
+ </description>
+ </method>
+ <method name="get_texture" qualifiers="const">
+ <return type="RID" />
+ <param index="0" name="context" type="StringName" />
+ <param index="1" name="name" type="StringName" />
+ <description>
+ Returns a cached texture with this name.
+ </description>
+ </method>
+ <method name="get_texture_format" qualifiers="const">
+ <return type="RDTextureFormat" />
+ <param index="0" name="context" type="StringName" />
+ <param index="1" name="name" type="StringName" />
+ <description>
+ Returns the texture format information with which a cached texture was created.
+ </description>
+ </method>
+ <method name="get_texture_slice">
+ <return type="RID" />
+ <param index="0" name="context" type="StringName" />
+ <param index="1" name="name" type="StringName" />
+ <param index="2" name="layer" type="int" />
+ <param index="3" name="mipmap" type="int" />
+ <param index="4" name="layers" type="int" />
+ <param index="5" name="mipmaps" type="int" />
+ <description>
+ Returns a specific slice (layer or mipmap) for a cached texture.
+ </description>
+ </method>
+ <method name="get_texture_slice_size">
+ <return type="Vector2i" />
+ <param index="0" name="context" type="StringName" />
+ <param index="1" name="name" type="StringName" />
+ <param index="2" name="mipmap" type="int" />
+ <description>
+ Returns the texture size of a given slice of a cached texture.
+ </description>
+ </method>
+ <method name="get_use_taa" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if TAA is enabled.
+ </description>
+ </method>
+ <method name="get_velocity_layer">
+ <return type="RID" />
+ <param index="0" name="layer" type="int" />
+ <description>
+ Returns the specified layer from the velocity texture we are rendering 3D content to.
+ </description>
+ </method>
+ <method name="get_velocity_texture">
+ <return type="RID" />
+ <description>
+ Returns the velocity texture we are rendering 3D content to. If multiview is used this will be a texture array with all views.
+ </description>
+ </method>
+ <method name="get_view_count" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the view count for the associated viewport.
+ </description>
+ </method>
+ <method name="has_texture" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="context" type="StringName" />
+ <param index="1" name="name" type="StringName" />
+ <description>
+ Returns [code]true[/code] if a cached texture exists for this name.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index ab993c372a..e4e194adf0 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="32767" />
+ <param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<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="32767" />
<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="32767" />
<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="32767" />
<description>
Finishes a list of compute commands created with the [code]compute_*[/code] methods.
</description>
@@ -214,7 +214,7 @@
<param index="0" name="screen" type="int" default="0" />
<param index="1" name="clear_color" type="Color" default="Color(0, 0, 0, 1)" />
<description>
- High-level variant of [method draw_list_begin], with the parameters automtaically being adjusted for drawing onto the window specified by the [param screen] ID.
+ High-level variant of [method draw_list_begin], with the parameters automatically being adjusted for drawing onto the window specified by the [param screen] ID.
[b]Note:[/b] Cannot be used with local RenderingDevices, as these don't have a screen. If called on a local RenderingDevice, [method draw_list_begin_for_screen] returns [constant INVALID_ID].
</description>
</method>
@@ -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="32767" />
<description>
Finishes a list of raster drawing commands created with the [code]draw_*[/code] methods.
</description>
@@ -619,6 +619,7 @@
<method name="shader_create_from_bytecode">
<return type="RID" />
<param index="0" name="binary_data" type="PackedByteArray" />
+ <param index="1" name="placeholder_rid" type="RID" default="RID()" />
<description>
Creates a new shader instance from a binary compiled shader. It can be accessed with the RID that is returned.
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. See also [method shader_compile_binary_from_spirv] and [method shader_create_from_spirv].
@@ -633,6 +634,12 @@
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method. See also [method shader_compile_spirv_from_source] and [method shader_create_from_bytecode].
</description>
</method>
+ <method name="shader_create_placeholder">
+ <return type="RID" />
+ <description>
+ Create a placeholder RID by allocating an RID without initializing it for use in [method shader_create_from_bytecode]. This allows you to create an RID for a shader and pass it around, but defer compiling the shader to a later time.
+ </description>
+ </method>
<method name="shader_get_vertex_input_attribute_mask">
<return type="int" />
<param index="0" name="shader" type="RID" />
@@ -682,7 +689,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="32767" />
<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 +706,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="32767" />
<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.
@@ -752,6 +759,13 @@
[b]Note:[/b] [param texture] requires the [constant TEXTURE_USAGE_CAN_COPY_FROM_BIT] to be retrieved. Otherwise, an error is printed and a empty [PackedByteArray] is returned.
</description>
</method>
+ <method name="texture_get_format">
+ <return type="RDTextureFormat" />
+ <param index="0" name="texture" type="RID" />
+ <description>
+ Returns the data format used to create this texture.
+ </description>
+ </method>
<method name="texture_get_native_handle">
<return type="int" />
<param index="0" name="texture" type="RID" />
@@ -786,7 +800,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="32767" />
<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 +817,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="32767" />
<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,8 +1594,11 @@
<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_FRAGMENT" value="8" enum="BarrierMask" is_bitfield="true">
+ Fragment shader barrier mask.
</constant>
<constant name="BARRIER_MASK_COMPUTE" value="2" enum="BarrierMask" is_bitfield="true">
Compute barrier mask.
@@ -1589,10 +1606,13 @@
<constant name="BARRIER_MASK_TRANSFER" value="4" enum="BarrierMask" is_bitfield="true">
Transfer barrier mask.
</constant>
- <constant name="BARRIER_MASK_ALL_BARRIERS" value="7" 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 name="BARRIER_MASK_RASTER" value="9" 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="32767" enum="BarrierMask" is_bitfield="true">
+ Barrier mask for all types (vertex, fragment, compute, transfer).
</constant>
- <constant name="BARRIER_MASK_NO_BARRIER" value="8" enum="BarrierMask" is_bitfield="true">
+ <constant name="BARRIER_MASK_NO_BARRIER" value="32768" 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/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 48001b3562..02c1286392 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -27,6 +27,13 @@
Bakes the material data of the Mesh passed in the [param base] parameter with optional [param material_overrides] to a set of [Image]s of size [param image_size]. Returns an array of [Image]s containing material properties as specified in [enum BakeChannels].
</description>
</method>
+ <method name="call_on_render_thread">
+ <return type="void" />
+ <param index="0" name="callable" type="Callable" />
+ <description>
+ As the RenderingServer actual logic may run on an separate thread, accessing its internals from the main (or any other) thread will result in errors. To make it easier to run code that can safely access the rendering internals (such as [RenderingDevice] and similar RD classes), push a callable via this function so it will be executed on the render thread.
+ </description>
+ </method>
<method name="camera_attributes_create">
<return type="RID" />
<description>
@@ -3281,6 +3288,13 @@
[b]Note:[/b] The [param texture] must have the same width, height, depth and format as the current texture data. Otherwise, an error will be printed and the original texture won't be modified. If you need to use different width, height, depth or format, use [method texture_replace] instead.
</description>
</method>
+ <method name="texture_get_format" qualifiers="const">
+ <return type="int" enum="Image.Format" />
+ <param index="0" name="texture" type="RID" />
+ <description>
+ Returns the [enum Image.Format] for the texture.
+ </description>
+ </method>
<method name="texture_get_native_handle" qualifiers="const">
<return type="int" />
<param index="0" name="texture" type="RID" />
@@ -3319,6 +3333,14 @@
[i]Deprecated.[/i] ProxyTexture was removed in Godot 4, so this method cannot be used anymore.
</description>
</method>
+ <method name="texture_rd_create">
+ <return type="RID" />
+ <param index="0" name="rd_texture" type="RID" />
+ <param index="1" name="layer_type" type="int" enum="RenderingServer.TextureLayeredType" default="0" />
+ <description>
+ Creates a new texture object based on a texture created directly on the [RenderingDevice]. If the texture contains layers, [param layer_type] is used to define the layer type.
+ </description>
+ </method>
<method name="texture_replace">
<return type="void" />
<param index="0" name="texture" type="RID" />
@@ -4628,6 +4650,8 @@
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS" value="10" enum="ViewportDebugDraw">
Draws the shadow atlas that stores shadows from [DirectionalLight3D]s in the upper left quadrant of the [Viewport].
+ The slice of the camera frustum related to the shadow map cascade is superimposed to visualize coverage. The color of each slice matches the colors used for [constant VIEWPORT_DEBUG_DRAW_PSSM_SPLITS]. When shadow cascades are blended the overlap is taken into account when drawing the frustum slices.
+ The last cascade shows all frustum slices to illustrate the coverage of all slices.
</constant>
<constant name="VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE" value="11" enum="ViewportDebugDraw">
Draws the estimated scene luminance. This is a 1×1 texture that is generated when autoexposure is enabled to control the scene's exposure.
diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml
index 83a878bada..2a2239f660 100644
--- a/doc/classes/Resource.xml
+++ b/doc/classes/Resource.xml
@@ -32,7 +32,7 @@
<method name="emit_changed">
<return type="void" />
<description>
- Emits the [signal changed] signal. This method is called automatically for built-in resources.
+ Emits the [signal changed] signal. This method is called automatically for some built-in resources.
[b]Note:[/b] For custom resources, it's recommended to call this method whenever a meaningful change occurs, such as a modified property. This ensures that custom [Object]s depending on the resource are properly updated.
[codeblock]
var damage:
diff --git a/doc/classes/ResourceImporterBMFont.xml b/doc/classes/ResourceImporterBMFont.xml
new file mode 100644
index 0000000000..3ec2e4afb7
--- /dev/null
+++ b/doc/classes/ResourceImporterBMFont.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterBMFont" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports a bitmap font in the BMFont ([code].fnt[/code]) format.
+ </brief_description>
+ <description>
+ The BMFont format is a format created by the [url=https://www.angelcode.com/products/bmfont/]BMFont[/url] program. Many BMFont-compatible programs also exist, like [url=https://www.bmglyph.com/]BMGlyph[/url].
+ Compared to [ResourceImporterImageFont], [ResourceImporterBMFont] supports bitmap fonts with varying glyph widths/heights.
+ See also [ResourceImporterDynamicFont].
+ </description>
+ <tutorials>
+ <link title="Bitmap fonts - Using fonts">$DOCS_URL/tutorials/ui/gui_using_fonts.html#bitmap-fonts</link>
+ </tutorials>
+ <members>
+ <member name="compress" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], uses lossless compression for the resulting font.
+ </member>
+ <member name="fallbacks" type="Array" setter="" getter="" default="[]">
+ List of font fallbacks to use if a glyph isn't found in this bitmap font. Fonts at the beginning of the array are attempted first.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceImporterBitMap.xml b/doc/classes/ResourceImporterBitMap.xml
new file mode 100644
index 0000000000..e54947ebb6
--- /dev/null
+++ b/doc/classes/ResourceImporterBitMap.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterBitMap" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports a [BitMap] resource (2D array of boolean values).
+ </brief_description>
+ <description>
+ [BitMap] resources are typically used as click masks in [TextureButton] and [TouchScreenButton].
+ </description>
+ <tutorials>
+ <link title="Importing images">$DOCS_URL/tutorials/assets_pipeline/importing_images.html</link>
+ </tutorials>
+ <members>
+ <member name="create_from" type="int" setter="" getter="" default="0">
+ The data source to use for generating the bitmap.
+ [b]Black &amp; White:[/b] Pixels whose HSV value is greater than the [member threshold] will be considered as "enabled" (bit is [code]true[/code]). If the pixel is lower than or equal to the threshold, it will be considered as "disabled" (bit is [code]false[/code]).
+ [b]Alpha:[/b] Pixels whose alpha value is greater than the [member threshold] will be considered as "enabled" (bit is [code]true[/code]). If the pixel is lower than or equal to the threshold, it will be considered as "disabled" (bit is [code]false[/code]).
+ </member>
+ <member name="threshold" type="float" setter="" getter="" default="0.5">
+ The threshold to use to determine which bits should be considered enabled or disabled. See also [member create_from].
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceImporterCSVTranslation.xml b/doc/classes/ResourceImporterCSVTranslation.xml
new file mode 100644
index 0000000000..e34f90d4ca
--- /dev/null
+++ b/doc/classes/ResourceImporterCSVTranslation.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterCSVTranslation" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports comma-separated values
+ </brief_description>
+ <description>
+ Comma-separated values are a plain text table storage format. The format's simplicity makes it easy to edit in any text editor or spreadsheet software. This makes it a common choice for game localization.
+ [b]Example CSV file:[/b]
+ [codeblock]
+ keys,en,es,ja
+ GREET,"Hello, friend!","Hola, amigo!",こんにちは
+ ASK,How are you?,Cómo está?,元気ですか
+ BYE,Goodbye,Adiós,さようなら
+ QUOTE,"""Hello"" said the man.","""Hola"" dijo el hombre.",「こんにちは」男は言いました
+ [/codeblock]
+ </description>
+ <tutorials>
+ <link title="Importing translations">https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/importing_translations.html</link>
+ </tutorials>
+ <members>
+ <member name="compress" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], creates an [OptimizedTranslation] instead of a [Translation]. This makes the resulting file smaller at the cost of a small CPU overhead.
+ </member>
+ <member name="delimiter" type="int" setter="" getter="" default="0">
+ The delimiter to use in the CSV file. The default value matches the common CSV convention. Tab-separated values are sometimes called TSV files.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceImporterDynamicFont.xml b/doc/classes/ResourceImporterDynamicFont.xml
new file mode 100644
index 0000000000..437b3c8a96
--- /dev/null
+++ b/doc/classes/ResourceImporterDynamicFont.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterDynamicFont" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports a TTF, TTC, OTF, OTC, WOFF or WOFF2 font file for font rendering that adapts to any size.
+ </brief_description>
+ <description>
+ Unlike bitmap fonts, dynamic fonts can be resized to any size and still look crisp. Dynamic fonts also optionally support MSDF font rendering, which allows for run-time scale changes with no re-rasterization cost.
+ While WOFF and especially WOFF2 tend to result in smaller file sizes, there is no universally "better" font format. In most situations, it's recommended to use the font format that was shipped on the font developer's website.
+ See also [ResourceImporterBMFont] and [ResourceImporterImageFont].
+ </description>
+ <tutorials>
+ <link title="Dynamic fonts - Using fonts">$DOCS_URL/tutorials/ui/gui_using_fonts.html#dynamic-fonts</link>
+ </tutorials>
+ <members>
+ <member name="allow_system_fallback" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], automatically use system fonts as a fallback if a glyph isn't found in this dynamic font. This makes supporting CJK characters or emoji more straightforward, as you don't need to include a CJK/emoji font in your project. See also [member fallbacks].
+ [b]Note:[/b] The appearance of system fonts varies across platforms. Loading system fonts is only supported on Windows, macOS, Linux, Android and iOS.
+ </member>
+ <member name="antialiasing" type="int" setter="" getter="" default="1">
+ The font antialiasing method to use.
+ [b]Disabled:[/b] Most suited for pixel art fonts, although you do not [i]have[/i] to change the antialiasing from the default [b]Grayscale[/b] if the font file was well-created and the font is used at an integer multiple of its intended size. If pixel art fonts have a bad appearance at their intended size, try setting [member subpixel_positioning] to [b]Disabled[/b] instead.
+ [b]Grayscale:[/b] Use grayscale antialiasing. This is the approach used by the operating system on macOS, Android and iOS.
+ [b]LCD Subpixel:[/b] Use antialiasing with subpixel patterns to make fonts sharper on LCD displays. This is the approach used by the operating system on Windows and most Linux distributions. The downside is that this can introduce "fringing" on edges, especially on display technologies that don't use standard RGB subpixels (such as OLED displays). The LCD subpixel layout is globally controlled by [member ProjectSettings.gui/theme/lcd_subpixel_layout], which also allows falling back to grayscale antialiasing.
+ </member>
+ <member name="compress" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], uses lossless compression for the resulting font.
+ </member>
+ <member name="fallbacks" type="Array" setter="" getter="" default="[]">
+ List of font fallbacks to use if a glyph isn't found in this dynamic font. Fonts at the beginning of the array are attempted first, but fallback fonts that don't support the glyph's language and script are attempted last (see [member language_support] and [member script_support]). See also [member allow_system_fallback].
+ </member>
+ <member name="force_autohinter" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], forces generation of hinting data for the font using [url=https://freetype.org/]FreeType[/url]'s autohinter. This will make [member hinting] effective with fonts that don't include hinting data.
+ </member>
+ <member name="generate_mipmaps" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], this font will have mipmaps generated. This prevents text from looking grainy when a [Control] is scaled down, or when a [Label3D] is viewed from a long distance (if [member Label3D.texture_filter] is set to a mode that displays mipmaps).
+ Enabling [member generate_mipmaps] increases font generation time and memory usage. Only enable this setting if you actually need it.
+ </member>
+ <member name="hinting" type="int" setter="" getter="" default="1">
+ The hinting mode to use. This controls how aggressively glyph edges should be snapped to pixels when rasterizing the font. Depending on personal preference, you may prefer using one hinting mode over the other. Hinting modes other than [b]None[/b] are only effective if the font contains hinting data (see [member force_autohinter]).
+ [b]None:[/b] Smoothest appearance, which can make the font look blurry at small sizes.
+ [b]Light:[/b] Sharp result by snapping glyph edges to pixels on the Y axis only.
+ [b]Full:[/b] Sharpest by snapping glyph edges to pixels on both X and Y axes.
+ </member>
+ <member name="language_support" type="Dictionary" setter="" getter="" default="{}">
+ Override the list of languages supported by this font. If left empty, this is supplied by the font metadata. There is usually no need to change this. See also [member script_support].
+ </member>
+ <member name="msdf_pixel_range" type="int" setter="" getter="" default="8">
+ The width of the range around the shape between the minimum and maximum representable signed distance. If using font outlines, [member msdf_pixel_range] must be set to at least [i]twice[/i] the size of the largest font outline. The default [member msdf_pixel_range] value of [code]8[/code] allows outline sizes up to [code]4[/code] to look correct.
+ </member>
+ <member name="msdf_size" type="int" setter="" getter="" default="48">
+ Source font size used to generate MSDF textures. Higher values allow for more precision, but are slower to render and require more memory. Only increase this value if you notice a visible lack of precision in glyph rendering. Only effective if [member multichannel_signed_distance_field] is [code]true[/code].
+ </member>
+ <member name="multichannel_signed_distance_field" type="bool" setter="" getter="" default="false">
+ If set to [code]true[/code], the default font will use multichannel signed distance field (MSDF) for crisp rendering at any size. Since this approach does not rely on rasterizing the font every time its size changes, this allows for resizing the font in real-time without any performance penalty. Text will also not look grainy for [Control]s that are scaled down (or for [Label3D]s viewed from a long distance).
+ MSDF font rendering can be combined with [member generate_mipmaps] to further improve font rendering quality when scaled down.
+ </member>
+ <member name="opentype_features" type="Dictionary" setter="" getter="" default="{}">
+ The OpenType features to enable, disable or set a value for this font. This can be used to enable optional features provided by the font, such as ligatures or alternative glyphs. The list of supported OpenType features varies on a per-font basis.
+ </member>
+ <member name="oversampling" type="float" setter="" getter="" default="0.0">
+ If set to a value greater than [code]0.0[/code], overrides the oversampling factor for the font. This can be used to render the font at a higher or lower resolution than intended without affecting its physical size. In most cases, this should be left at [code]0.0[/code].
+ </member>
+ <member name="preload" type="Array" setter="" getter="" default="[]">
+ The glyph ranges to prerender. This can avoid stuttering during gameplay when new characters need to be rendered, especially if [member subpixel_positioning] is enabled. The downside of using preloading is that initial project load times will increase, as well as memory usage.
+ </member>
+ <member name="script_support" type="Dictionary" setter="" getter="" default="{}">
+ Override the list of language scripts supported by this font. If left empty, this is supplied by the font metadata. There is usually no need to change this. See also [member language_support].
+ </member>
+ <member name="subpixel_positioning" type="int" setter="" getter="" default="1">
+ Subpixel positioning improves font rendering appearance, especially at smaller font sizes. The downside is that it takes more time to initially render the font, which can cause stuttering during gameplay, especially if used with large font sizes. This should be set to [b]Disabled[/b] for fonts with a pixel art appearance.
+ [b]Disabled:[/b] No subpixel positioning. Lowest quality, fastest rendering.
+ [b]Auto:[/b] Use subpixel positioning at small font sizes (the chosen quality varies depending on font size). Large fonts will not use subpixel positioning. This is a good tradeoff between performance and quality.
+ [b]One Half of a Pixel:[/b] Always perform intermediate subpixel positioning regardless of font size. High quality, slow rendering.
+ [b]One Quarter of a Pixel:[/b] Always perform precise subpixel positioning regardless of font size. Highest quality, slowest rendering.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceImporterImage.xml b/doc/classes/ResourceImporterImage.xml
new file mode 100644
index 0000000000..8fbec8af01
--- /dev/null
+++ b/doc/classes/ResourceImporterImage.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterImage" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports a image for use in scripting, with no rendering capabilities.
+ </brief_description>
+ <description>
+ This importer imports [Image] resources, as opposed to [CompressedTexture2D]. If you need to render the image in 2D or 3D, use [ResourceImporterTexture] instead.
+ </description>
+ <tutorials>
+ <link title="Importing images">$DOCS_URL/tutorials/assets_pipeline/importing_images.html</link>
+ </tutorials>
+</class>
diff --git a/doc/classes/ResourceImporterImageFont.xml b/doc/classes/ResourceImporterImageFont.xml
new file mode 100644
index 0000000000..96598f78df
--- /dev/null
+++ b/doc/classes/ResourceImporterImageFont.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterImageFont" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports a fixed-width bitmap font where all glyphs have the same width and height.
+ </brief_description>
+ <description>
+ This image-based workflow can be easier to use than [ResourceImporterBMFont], but it requires all glyphs to have the same width and height. This makes [ResourceImporterImageFont] most suited to fixed-width fonts.
+ See also [ResourceImporterDynamicFont].
+ </description>
+ <tutorials>
+ <link title="Bitmap fonts - Using fonts">$DOCS_URL/tutorials/ui/gui_using_fonts.html#bitmap-fonts</link>
+ </tutorials>
+ <members>
+ <member name="character_margin" type="Rect2i" setter="" getter="" default="Rect2i(0, 0, 0, 0)">
+ Margin applied around every imported glyph. If your font image contains guides (in the form of lines between glyphs) or if spacing between characters appears incorrect, try adjusting [member character_margin].
+ </member>
+ <member name="character_ranges" type="PackedStringArray" setter="" getter="" default="PackedStringArray()">
+ The character ranges to import from the font image. This is an array that maps each position on the image (in tile coordinates, not pixels). The font atlas is traversed from left to right and top to bottom. Characters can be specified with decimal numbers (127), hexadecimal numbers ([code]0x007f[/code]) or between single quotes ([code]'~'[/code]). Ranges can be specified with a hyphen between characters.
+ For instance, [code]0-127[/code] (or [code]0x0000-0x007f[/code]) denotes the full ASCII range. As another example, [code]' '-'~'[/code] is equivalent to [code]32-127[/code] and denotes the range of printable (visible) ASCII characters.
+ Make sure [member character_ranges] doesn't exceed the number of [member columns] * [member rows] defined. Otherwise, the font will fail to import.
+ </member>
+ <member name="columns" type="int" setter="" getter="" default="1">
+ Number of columns in the font image. See also [member rows].
+ </member>
+ <member name="compress" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], uses lossless compression for the resulting font.
+ </member>
+ <member name="fallbacks" type="Array" setter="" getter="" default="[]">
+ List of font fallbacks to use if a glyph isn't found in this bitmap font. Fonts at the beginning of the array are attempted first.
+ </member>
+ <member name="image_margin" type="Rect2i" setter="" getter="" default="Rect2i(0, 0, 0, 0)">
+ Margin to cut on the sides of the entire image. This can be used to cut parts of the image that contain attribution information or similar.
+ </member>
+ <member name="rows" type="int" setter="" getter="" default="1">
+ Number of rows in the font image. See also [member columns].
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceImporterLayeredTexture.xml b/doc/classes/ResourceImporterLayeredTexture.xml
new file mode 100644
index 0000000000..6d3c868e3d
--- /dev/null
+++ b/doc/classes/ResourceImporterLayeredTexture.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterLayeredTexture" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports a 3-dimensional texture ([Texture3D]), a [Texture2DArray], a [Cubemap] or a [CubemapArray].
+ </brief_description>
+ <description>
+ This imports a 3-dimensional texture, which can then be used in custom shaders, as a [FogMaterial] density map or as a [GPUParticlesAttractorVectorField3D]. See also [ResourceImporterTexture] and [ResourceImporterTextureAtlas].
+ </description>
+ <tutorials>
+ <link title="Importing images">$DOCS_URL/tutorials/assets_pipeline/importing_images.html</link>
+ </tutorials>
+ <members>
+ <member name="compress/channel_pack" type="int" setter="" getter="" default="0">
+ Controls how color channels should be used in the imported texture.
+ [b]sRGB Friendly:[/b], prevents the RG color format from being used, as it does not support sRGB color.
+ [b]Optimized:[/b], allows the RG color format to be used if the texture does not use the blue channel. This reduces memory usage if the texture's blue channel can be discarded (all pixels must have a blue value of [code]0[/code]).
+ [b]Normal Map (RG Channels):[/b] This forces all layers from the texture to be imported with the RG color format to reduce memory usage, with only the red and green channels preserved. This only has an effect on textures with the VRAM Compressed or Basis Universal compression modes. This mode is only available in layered textures ([Cubemap], [CubemapArray], [Texture2DArray] and [Texture3D]).
+ </member>
+ <member name="compress/hdr_compression" type="int" setter="" getter="" default="1">
+ Controls how VRAM compression should be performed for HDR images.
+ [b]Disabled:[/b] Never use VRAM compression for HDR textures, regardless of whether they're opaque or transparent. Instead, the texture is converted to RGBE9995 (9-bits per channel + 5-bit exponent = 32 bits per pixel) to reduce memory usage compared to a half-float or single-precision float image format.
+ [b]Opaque Only:[/b] Only uses VRAM compression for opaque HDR textures. This is due to a limitation of HDR formats, as there is no VRAM-compressed HDR format that supports transparency at the same time.
+ [b]Always:[/b] Force VRAM compression even for HDR textures with an alpha channel. To perform this, the alpha channel is discarded on import.
+ [b]Note:[/b] Only effective on Radiance HDR ([code].hdr[/code]) and OpenEXR ([code].exr[/code]) images.
+ </member>
+ <member name="compress/high_quality" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], uses BPTC compression on desktop platforms and ASTC compression on mobile platforms. When using BPTC, BC7 is used for SDR textures and BC6H is used for HDR textures.
+ If [code]false[/code], uses the faster but lower-quality S3TC compression on desktop platforms and ETC2 on mobile/web platforms. When using S3TC, DXT1 (BC1) is used for opaque textures and DXT5 (BC3) is used for transparent or normal map (RGTC) textures.
+ BPTC and ASTC support VRAM compression for HDR textures, but S3TC and ETC2 do not (see [member compress/hdr_compression]).
+ </member>
+ <member name="compress/lossy_quality" type="float" setter="" getter="" default="0.7">
+ The quality to use when using the [b]Lossy[/b] compression mode. Higher values result in better quality, at the cost of larger file sizes. Lossy quality does not affect memory usage of the imported texture, only its file size on disk.
+ </member>
+ <member name="compress/mode" type="int" setter="" getter="" default="1">
+ The compression mode to use. Each compression mode provides a different tradeoff:
+ [b]Lossless[/b]: Original quality, high memory usage, high size on disk, fast import.
+ [b]Lossy:[/b] Reduced quality, high memory usage, low size on disk, fast import.
+ [b]VRAM Compressed:[/b] Reduced quality, low memory usage, low size on disk, slowest import. Only use for textures in 3D scenes, not for 2D elements.
+ [b]VRAM Uncompressed:[/b] Original quality, high memory usage, highest size on disk, fastest import.
+ [b]Basis Universal:[/b] Reduced quality, low memory usage, lowest size on disk, slow import. Only use for textures in 3D scenes, not for 2D elements.
+ See [url=https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/importing_images.html#compress-mode]Compress mode[/url] in the manual for more details.
+ </member>
+ <member name="mipmaps/generate" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], smaller versions of the texture are generated on import. For example, a 64×64 texture will generate 6 mipmaps (32×32, 16×16, 8×8, 4×4, 2×2, 1×1). This has several benefits:
+ - Textures will not become grainy in the distance (in 3D), or if scaled down due to [Camera2D] zoom or [CanvasItem] scale (in 2D).
+ - Performance will improve if the texture is displayed in the distance, since sampling smaller versions of the original texture is faster and requires less memory bandwidth.
+ The downside of mipmaps is that they increase memory usage by roughly 33% (for [Texture2DArray], [Cubemap] and [CubemapArray]) or 14% (for [Texture3D]).
+ It's recommended to enable mipmaps in 3D. However, in 2D, this should only be enabled if your project visibly benefits from having mipmaps enabled. If the camera never zooms out significantly, there won't be a benefit to enabling mipmaps but memory usage will increase.
+ </member>
+ <member name="mipmaps/limit" type="int" setter="" getter="" default="-1">
+ Unimplemented. This currently has no effect when changed.
+ </member>
+ <member name="slices/arrangement" type="int" setter="" getter="" default="1">
+ Controls how the cubemap's texture is internally laid out. When using high-resolution cubemaps, [b]2×3[/b] and [b]3×2[/b] are less prone to exceeding hardware texture size limits compared to [b]1×6[/b] and [b]6×1[/b].
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceImporterOBJ.xml b/doc/classes/ResourceImporterOBJ.xml
new file mode 100644
index 0000000000..70b57b4d2d
--- /dev/null
+++ b/doc/classes/ResourceImporterOBJ.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterOBJ" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports an OBJ 3D model as a standalone [Mesh] or scene.
+ </brief_description>
+ <description>
+ Unlike [ResourceImporterScene], [ResourceImporterOBJ] will import a single [Mesh] resource by default instead of importing a [PackedScene]. This makes it easier to use the [Mesh] resource in nodes that expect direct [Mesh] resources, such as [GridMap], [GPUParticles3D] or [CPUParticles3D]. Note that it is still possible to save mesh resources from 3D scenes using the [b]Advanced Import Settings[/b] dialog, regardless of the source format.
+ See also [ResourceImporterScene], which is used for more advanced 3D formats such as glTF.
+ </description>
+ <tutorials>
+ <link title="Importing 3D scenes">$DOCS_URL/tutorials/assets_pipeline/importing_scenes.html</link>
+ </tutorials>
+ <members>
+ <member name="generate_tangents" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], generate vertex tangents using [url=http://www.mikktspace.com/]Mikktspace[/url] if the source mesh doesn't have tangent data. When possible, it's recommended to let the 3D modeling software generate tangents on export instead on relying on this option. Tangents are required for correct display of normal and height maps, along with any material/shader features that require tangents.
+ If you don't need material features that require tangents, disabling this can reduce output file size and speed up importing if the source 3D file doesn't contain tangents.
+ </member>
+ <member name="offset_mesh" type="Vector3" setter="" getter="" default="Vector3(0, 0, 0)">
+ Offsets the mesh's data by the specified value. This can be used to work around misaligned meshes without having to modify the source file.
+ </member>
+ <member name="optimize_mesh" type="bool" setter="" getter="" default="true">
+ Unused parameter. This currently has no effect.
+ </member>
+ <member name="scale_mesh" type="Vector3" setter="" getter="" default="Vector3(1, 1, 1)">
+ Scales the mesh's data by the specified value. This can be used to work around misscaled meshes without having to modify the source file.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceImporterScene.xml b/doc/classes/ResourceImporterScene.xml
new file mode 100644
index 0000000000..b7d42b5a09
--- /dev/null
+++ b/doc/classes/ResourceImporterScene.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterScene" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports a glTF, FBX, Collada or Blender 3D scene.
+ </brief_description>
+ <description>
+ See also [ResourceImporterOBJ], which is used for OBJ models that can be imported as a standalone [Mesh] or a scene.
+ Additional options (such as extracting individual meshes or materials to files) are available in the [b]Advanced Import Settings[/b] dialog. This dialog can be accessed by double-clicking a 3D scene in the FileSystem dock or by selecting a 3D scene in the FileSystem dock, going to the Import dock and choosing [b]Advanced[/b].
+ [b]Note:[/b] [ResourceImporterScene] is [i]not[/i] used for [PackedScene]s, such as [code].tscn[/code] and [code].scn[/code] files.
+ </description>
+ <tutorials>
+ <link title="Importing 3D scenes">$DOCS_URL/tutorials/assets_pipeline/importing_scenes.html</link>
+ </tutorials>
+ <members>
+ <member name="_subresources" type="Dictionary" setter="" getter="" default="{}">
+ Contains properties for the scene's subresources. This is an internal option which is not visible in the Import dock.
+ </member>
+ <member name="animation/fps" type="float" setter="" getter="" default="30">
+ The number of frames per second to use for baking animation curves to a series of points with linear interpolation. It's recommended to configure this value to match the value you're using as a baseline in your 3D modeling software. Higher values result in more precise animation with fast movement changes, at the cost of higher file sizes and memory usage. Thanks to interpolation, there is usually not much benefit in going above 30 FPS (as the animation will still appear smooth at higher rendering framerates).
+ </member>
+ <member name="animation/import" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], import animations from the 3D scene.
+ </member>
+ <member name="animation/remove_immutable_tracks" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], remove animation tracks that only contain default values. This can reduce output file size and memory usage with certain 3D scenes, depending on the contents of their animation tracks.
+ </member>
+ <member name="animation/trimming" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], trim the beginning and end of animations if there are no keyframe changes. This can reduce output file size and memory usage with certain 3D scenes, depending on the contents of their animation tracks.
+ </member>
+ <member name="import_script/path" type="String" setter="" getter="" default="&quot;&quot;">
+ Path to an import script, which can run code after the import process has completed for custom processing. See [url=$DOCS_URL/tutorials/assets_pipeline/importing_scenes.html#doc-importing-3d-scenes-import-script]Using import scripts for automation[/url] for more information.
+ </member>
+ <member name="meshes/create_shadow_meshes" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], enables the generation of shadow meshes on import. This optimizes shadow rendering without reducing quality by welding vertices together when possible. This in turn reduces the memory bandwidth required to render shadows. Shadow mesh generation currently doesn't support using a lower detail level than the source mesh (but shadow rendering will make use of LODs when relevant).
+ </member>
+ <member name="meshes/ensure_tangents" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], generate vertex tangents using [url=http://www.mikktspace.com/]Mikktspace[/url] if the input meshes don't have tangent data. When possible, it's recommended to let the 3D modeling software generate tangents on export instead on relying on this option. Tangents are required for correct display of normal and height maps, along with any material/shader features that require tangents.
+ If you don't need material features that require tangents, disabling this can reduce output file size and speed up importing if the source 3D file doesn't contain tangents.
+ </member>
+ <member name="meshes/generate_lods" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], generates lower detail variants of the mesh which will be displayed in the distance to improve rendering performance. Not all meshes benefit from LOD, especially if they are never rendered from far away. Disabling this can reduce output file size and speed up importing. See [url=$DOCS_URL/tutorials/3d/mesh_lod.html#doc-mesh-lod]Mesh level of detail (LOD)[/url] for more information.
+ </member>
+ <member name="meshes/light_baking" type="int" setter="" getter="" default="1">
+ Configures the meshes' [member GeometryInstance3D.gi_mode] in the 3D scene. If set to [b]Static Lightmaps[/b], sets the meshes' GI mode to Static and generates UV2 on import for [LightmapGI] baking.
+ </member>
+ <member name="meshes/lightmap_texel_size" type="float" setter="" getter="" default="0.2">
+ Controls the size of each texel on the baked lightmap. A smaller value results in more precise lightmaps, at the cost of larger lightmap sizes and longer bake times.
+ [b]Note:[/b] Only effective if [member meshes/light_baking] is set to [b]Static Lightmaps[/b].
+ </member>
+ <member name="nodes/apply_root_scale" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], [member nodes/root_scale] will be applied on the meshes and animations directly, while keeping the root node's scale to the default [code](1, 1, 1)[/code]. This means that if you add a child node later on within the imported scene, it won't be scaled. If disabled, [member nodes/root_scale] will multiply the scale of the root node instead.
+ </member>
+ <member name="nodes/root_name" type="String" setter="" getter="" default="&quot;Scene Root&quot;">
+ The name of the root node in the imported scene. This is generally not noticeable when instancing the scene in the editor (or drag-and-dropping from the FileSystem dock), as the root node is renamed to match the filename in this case.
+ </member>
+ <member name="nodes/root_scale" type="float" setter="" getter="" default="1.0">
+ The scale of meshes and animations (if [member nodes/apply_root_scale] is [code]true[/code]), or the scale of the root node in the imported scene (if [member nodes/apply_root_scale] is [code]false[/code]).
+ </member>
+ <member name="nodes/root_type" type="String" setter="" getter="" default="&quot;Node3D&quot;">
+ The node type to use as a root node. Using node types that inherit from [Node3D] is recommended. Otherwise, you'll lose the ability to position the node directly in the 3D editor.
+ </member>
+ <member name="skins/use_named_skins" type="bool" setter="" getter="" default="true">
+ If checked, use named [Skin]s for animation. The [MeshInstance3D] node contains 3 properties of relevance here: a skeleton [NodePath] pointing to the [Skeleton3D] node (usually [code]..[/code]), a mesh, and a skin:
+ - The [Skeleton3D] node contains a list of bones with names, their pose and rest, a name and a parent bone.
+ - The mesh is all of the raw vertex data needed to display a mesh. In terms of the mesh, it knows how vertices are weight-painted and uses some internal numbering often imported from 3D modeling software.
+ - The skin contains the information necessary to bind this mesh onto this Skeleton3D. For every one of the internal bone IDs chosen by the 3D modeling software, it contains two things. Firstly, a matrix known as the Bind Pose Matrix, Inverse Bind Matrix, or IBM for short. Secondly, the [Skin] contains each bone's name (if [member skins/use_named_skins] is [code]true[/code]), or the bone's index within the [Skeleton3D] list (if [member skins/use_named_skins] is [code]false[/code]).
+ Together, this information is enough to tell Godot how to use the bone poses in the [Skeleton3D] node to render the mesh from each [MeshInstance3D]. Note that each [MeshInstance3D] may share binds, as is common in models exported from Blender, or each [MeshInstance3D] may use a separate [Skin] object, as is common in models exported from other tools such as Maya.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceImporterShaderFile.xml b/doc/classes/ResourceImporterShaderFile.xml
new file mode 100644
index 0000000000..85863758d8
--- /dev/null
+++ b/doc/classes/ResourceImporterShaderFile.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterShaderFile" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports native GLSL shaders (not Godot shaders) as a [RDShaderFile].
+ </brief_description>
+ <description>
+ This imports native GLSL shaders as [RDShaderFile] resources, for use with low-level [RenderingDevice] operations. This importer does [i]not[/i] handle [code].gdshader[/code] files.
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/doc/classes/ResourceImporterTexture.xml b/doc/classes/ResourceImporterTexture.xml
new file mode 100644
index 0000000000..b81ebf383a
--- /dev/null
+++ b/doc/classes/ResourceImporterTexture.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterTexture" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports an image for use in 2D or 3D rendering.
+ </brief_description>
+ <description>
+ This importer imports [CompressedTexture2D] resources. If you need to process the image in scripts in a more convenient way, use [ResourceImporterImage] instead. See also [ResourceImporterLayeredTexture].
+ </description>
+ <tutorials>
+ <link title="Importing images">$DOCS_URL/tutorials/assets_pipeline/importing_images.html</link>
+ </tutorials>
+ <members>
+ <member name="compress/channel_pack" type="int" setter="" getter="" default="0">
+ Controls how color channels should be used in the imported texture.
+ [b]sRGB Friendly:[/b] Prevents the RG color format from being used, as it does not support sRGB color.
+ [b]Optimized:[/b] Allows the RG color format to be used if the texture does not use the blue channel. This reduces memory usage if the texture's blue channel can be discarded (all pixels must have a blue value of [code]0[/code]).
+ </member>
+ <member name="compress/hdr_compression" type="int" setter="" getter="" default="1">
+ Controls how VRAM compression should be performed for HDR images.
+ [b]Disabled:[/b] Never use VRAM compression for HDR textures, regardless of whether they're opaque or transparent. Instead, the texture is converted to RGBE9995 (9-bits per channel + 5-bit exponent = 32 bits per pixel) to reduce memory usage compared to a half-float or single-precision float image format.
+ [b]Opaque Only:[/b] Only uses VRAM compression for opaque HDR textures. This is due to a limitation of HDR formats, as there is no VRAM-compressed HDR format that supports transparency at the same time.
+ [b]Always:[/b] Force VRAM compression even for HDR textures with an alpha channel. To perform this, the alpha channel is discarded on import.
+ [b]Note:[/b] Only effective on Radiance HDR ([code].hdr[/code]) and OpenEXR ([code].exr[/code]) images.
+ </member>
+ <member name="compress/high_quality" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], uses BPTC compression on desktop platforms and ASTC compression on mobile platforms. When using BPTC, BC7 is used for SDR textures and BC6H is used for HDR textures.
+ If [code]false[/code], uses the faster but lower-quality S3TC compression on desktop platforms and ETC2 on mobile/web platforms. When using S3TC, DXT1 (BC1) is used for opaque textures and DXT5 (BC3) is used for transparent or normal map (RGTC) textures.
+ BPTC and ASTC support VRAM compression for HDR textures, but S3TC and ETC2 do not (see [member compress/hdr_compression]).
+ </member>
+ <member name="compress/lossy_quality" type="float" setter="" getter="" default="0.7">
+ The quality to use when using the [b]Lossy[/b] compression mode. Higher values result in better quality, at the cost of larger file sizes. Lossy quality does not affect memory usage of the imported texture, only its file size on disk.
+ </member>
+ <member name="compress/mode" type="int" setter="" getter="" default="0">
+ The compression mode to use. Each compression mode provides a different tradeoff:
+ [b]Lossless[/b]: Original quality, high memory usage, high size on disk, fast import.
+ [b]Lossy:[/b] Reduced quality, high memory usage, low size on disk, fast import.
+ [b]VRAM Compressed:[/b] Reduced quality, low memory usage, low size on disk, slowest import. Only use for textures in 3D scenes, not for 2D elements.
+ [b]VRAM Uncompressed:[/b] Original quality, high memory usage, highest size on disk, fastest import.
+ [b]Basis Universal:[/b] Reduced quality, low memory usage, lowest size on disk, slow import. Only use for textures in 3D scenes, not for 2D elements.
+ See [url=https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/importing_images.html#compress-mode]Compress mode[/url] in the manual for more details.
+ </member>
+ <member name="compress/normal_map" type="int" setter="" getter="" default="0">
+ When using a texture as normal map, only the red and green channels are required. Given regular texture compression algorithms produce artifacts that don't look that nice in normal maps, the RGTC compression format is the best fit for this data. Forcing this option to Enable will make Godot import the image as RGTC compressed. By default, it's set to Detect. This means that if the texture is ever detected to be used as a normal map, it will be changed to Enable and reimported automatically.
+ Note that RGTC compression affects the resulting normal map image. You will have to adjust custom shaders that use the normal map's blue channel to take this into account. Built-in material shaders already ignore the blue channel in a normal map (regardless of the actual normal map's contents).
+ </member>
+ <member name="detect_3d/compress_to" type="int" setter="" getter="" default="1">
+ This changes the [member compress/mode] option that is used when a texture is detected as being used in 3D.
+ Changing this import option only has an effect if a texture is detected as being used in 3D. Changing this to [b]Disabled[/b] then reimporting will not change the existing compress mode on a texture (if it's detected to be used in 3D), but choosing [b]VRAM Compressed[/b] or [b]Basis Universal[/b] will.
+ </member>
+ <member name="editor/convert_colors_with_editor_theme" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], converts the imported image's colors to match [member EditorSettings.interface/theme/icon_and_font_color]. This assumes the image uses the exact same colors as [url=$DOCS_URL/contributing/development/editor/creating_icons.html]Godot's own color palette for editor icons[/url], with the source file designed for a dark editor theme. This should be enabled for editor plugin icons and custom class icons, but should be left disabled otherwise.
+ [b]Note:[/b] Only available for SVG images.
+ </member>
+ <member name="editor/scale_with_editor_scale" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], scales the imported image to match [member EditorSettings.interface/editor/custom_display_scale]. This should be enabled for editor plugin icons and custom class icons, but should be left disabled otherwise.
+ [b]Note:[/b] Only available for SVG images.
+ </member>
+ <member name="mipmaps/generate" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], smaller versions of the texture are generated on import. For example, a 64×64 texture will generate 6 mipmaps (32×32, 16×16, 8×8, 4×4, 2×2, 1×1). This has several benefits:
+ - Textures will not become grainy in the distance (in 3D), or if scaled down due to [Camera2D] zoom or [CanvasItem] scale (in 2D).
+ - Performance will improve if the texture is displayed in the distance, since sampling smaller versions of the original texture is faster and requires less memory bandwidth.
+ The downside of mipmaps is that they increase memory usage by roughly 33%.
+ It's recommended to enable mipmaps in 3D. However, in 2D, this should only be enabled if your project visibly benefits from having mipmaps enabled. If the camera never zooms out significantly, there won't be a benefit to enabling mipmaps but memory usage will increase.
+ </member>
+ <member name="mipmaps/limit" type="int" setter="" getter="" default="-1">
+ Unimplemented. This currently has no effect when changed.
+ </member>
+ <member name="process/fix_alpha_border" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], puts pixels of the same surrounding color in transition from transparent to opaque areas. For textures displayed with bilinear filtering, this helps mitigate the outline effect when exporting images from an image editor.
+ It's recommended to leave this enabled (as it is by default), unless this causes issues for a particular image.
+ </member>
+ <member name="process/hdr_as_srgb" type="bool" setter="" getter="" default="false">
+ Some HDR images you can find online may be broken and contain sRGB color data (instead of linear color data). It is advised not to use those files. If you absolutely have to, enabling [member process/hdr_as_srgb] will make them look correct.
+ [b]Warning:[/b] Enabling [member process/hdr_as_srgb] on well-formatted HDR images will cause the resulting image to look too dark, so leave this on [code]false[/code] if unsure.
+ </member>
+ <member name="process/hdr_clamp_exposure" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], clamps exposure in the imported high dynamic range images using a smart clamping formula (without introducing [i]visible[/i] clipping).
+ Some HDR panorama images you can find online may contain extremely bright pixels, due to being taken from real life sources without any clipping.
+ While these HDR panorama images are accurate to real life, this can cause the radiance map generated by Godot to contain sparkles when used as a background sky. This can be seen in material reflections (even on rough materials in extreme cases). Enabling [member process/hdr_clamp_exposure] can resolve this.
+ </member>
+ <member name="process/normal_map_invert_y" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], convert the normal map from Y- (DirectX-style) to Y+ (OpenGL-style) by inverting its green color channel. This is the normal map convention expected by Godot.
+ More information about normal maps (including a coordinate order table for popular engines) can be found [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details]here[/url].
+ </member>
+ <member name="process/premult_alpha" type="bool" setter="" getter="" default="false">
+ An alternative to fixing darkened borders with [member process/fix_alpha_border] is to use premultiplied alpha. By enabling this option, the texture will be converted to this format. A premultiplied alpha texture requires specific materials to be displayed correctly:
+ - In 2D, a [CanvasItemMaterial] will need to be created and configured to use the [constant CanvasItemMaterial.BLEND_MODE_PREMULT_ALPHA] blend mode on [CanvasItem]s that use this texture.
+ - In 3D, there is no support for premultiplied alpha blend mode yet, so this option is only suited for 2D.
+ </member>
+ <member name="process/size_limit" type="int" setter="" getter="" default="0">
+ If set to a value greater than [code]0[/code], the size of the texture is limited on import to a value smaller than or equal to the value specified here. For non-square textures, the size limit affects the longer dimension, with the shorter dimension scaled to preserve aspect ratio. Resizing is performed using cubic interpolation.
+ This can be used to reduce memory usage without affecting the source images, or avoid issues with textures not displaying on mobile/web platforms (as these usually can't display textures larger than 4096×4096).
+ </member>
+ <member name="roughness/mode" type="int" setter="" getter="" default="0">
+ The color channel to consider as a roughness map in this texture. Only effective if Roughness &gt; Src Normal is not empty.
+ </member>
+ <member name="roughness/src_normal" type="String" setter="" getter="" default="&quot;&quot;">
+ The path to the texture to consider as a normal map for roughness filtering on import. Specifying this can help decrease specular aliasing slightly in 3D.
+ Roughness filtering on import is only used in 3D rendering, not 2D.
+ </member>
+ <member name="svg/scale" type="float" setter="" getter="" default="1.0">
+ The scale the SVG should be rendered at, with [code]1.0[/code] being the original design size. Higher values result in a larger image. Note that unlike font oversampling, this affects the size the SVG is rendered at in 2D. See also [member editor/scale_with_editor_scale].
+ [b]Note:[/b] Only available for SVG images.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceImporterTextureAtlas.xml b/doc/classes/ResourceImporterTextureAtlas.xml
new file mode 100644
index 0000000000..9f3f642869
--- /dev/null
+++ b/doc/classes/ResourceImporterTextureAtlas.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterTextureAtlas" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports a collection of textures from a PNG image into an optimized [AtlasTexture] for 2D rendering.
+ </brief_description>
+ <description>
+ This imports a collection of textures from a PNG image into an [AtlasTexture] or 2D [ArrayMesh]. This can be used to save memory when importing 2D animations from spritesheets. Texture atlases are only supported in 2D rendering, not 3D. See also [ResourceImporterTexture] and [ResourceImporterLayeredTexture].
+ [b]Note:[/b] [ResourceImporterTextureAtlas] does not handle importing [TileSetAtlasSource], which is created using the [TileSet] editor instead.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="atlas_file" type="String" setter="" getter="" default="&quot;&quot;">
+ Path to the atlas spritesheet. This [i]must[/i] be set to valid path to a PNG image. Otherwise, the atlas will fail to import.
+ </member>
+ <member name="crop_to_region" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], discards empty areas from the atlas. This only affects final sprite positioning, not storage. See also [member trim_alpha_border_from_region].
+ [b]Note:[/b] Only effective if [member import_mode] is [b]Region[/b].
+ </member>
+ <member name="import_mode" type="int" setter="" getter="" default="0">
+ [b]Region:[/b] Imports the atlas in an [AtlasTexture] resource, which is rendered as a rectangle. This is fast to render, but transparent areas still have to be rendered if they can't be trimmed effectively by [member trim_alpha_border_from_region]. This can reduce performance when rendering large sprites on screen.
+ [b]Mesh:[/b] Imports the atlas as an [ArrayMesh] resource, keeping the original bitmap visible (but rendered as a polygon). This can be used to reduce fill rate when rendering large transparent sprites, at the cost of slower rendering if there are little to no transparent areas in the sprite.
+ </member>
+ <member name="trim_alpha_border_from_region" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], trims the region to exclude fully transparent pixels using a clipping rectangle (which is never rotated). This can be used to save memory. See also [member trim_alpha_border_from_region].
+ [b]Note:[/b] Only effective if [member import_mode] is [b]Region[/b].
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceImporterWAV.xml b/doc/classes/ResourceImporterWAV.xml
new file mode 100644
index 0000000000..5336c98d0f
--- /dev/null
+++ b/doc/classes/ResourceImporterWAV.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterWAV" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Imports a WAV audio file for playback.
+ </brief_description>
+ <description>
+ WAV is an uncompressed format, which can provide higher quality compared to Ogg Vorbis and MP3. It also has the lowest CPU cost to decode. This means high numbers of WAV sounds can be played at the same time, even on low-end deviceS.
+ </description>
+ <tutorials>
+ <link title="Importing audio samples">$DOCS_URL/tutorials/assets_pipeline/importing_audio_samples.html</link>
+ </tutorials>
+ <members>
+ <member name="compress/mode" type="int" setter="" getter="" default="0">
+ The compression mode to use on import.
+ [b]Disabled:[/b] Imports audio data without any compression. This results in the highest possible quality.
+ [b]RAM (Ima-ADPCM):[/b] Performs fast lossy compression on import. Low CPU cost, but quality is noticeably decreased compared to Ogg Vorbis or even MP3.
+ </member>
+ <member name="edit/loop_begin" type="int" setter="" getter="" default="0">
+ The begin loop point to use when [member edit/loop_mode] is [b]Forward[/b], [b]Ping-Pong[/b] or [b]Backward[/b]. This is set in seconds after the beginning of the audio file.
+ </member>
+ <member name="edit/loop_end" type="int" setter="" getter="" default="-1">
+ The end loop point to use when [member edit/loop_mode] is [b]Forward[/b], [b]Ping-Pong[/b] or [b]Backward[/b]. This is set in seconds after the beginning of the audio file. A value of [code]-1[/code] uses the end of the audio file as the end loop point.
+ </member>
+ <member name="edit/loop_mode" type="int" setter="" getter="" default="0">
+ Controls how audio should loop. This is automatically read from the WAV metadata on import.
+ [b]Disabled:[/b] Don't loop audio, even if metadata indicates the file should be played back looping.
+ [b]Forward:[/b] Standard audio looping.
+ [b]Ping-Pong:[/b] Play audio forward until it's done playing, then play it backward and repeat. This is similar to mirrored texture repeat, but for audio.
+ [b]Backward:[/b] Play audio in reverse and loop back to the end when done playing.
+ [b]Note:[/b] In [AudioStreamPlayer], the [signal AudioStreamPlayer.finished] signal won't be emitted for looping audio when it reaches the end of the audio file, as the audio will keep playing indefinitely.
+ </member>
+ <member name="edit/normalize" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], normalize the audio volume so that its peak volume is equal to 0 dB. When enabled, normalization will make audio sound louder depending on its original peak volume.
+ </member>
+ <member name="edit/trim" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], automatically trim the beginning and end of the audio if it's lower than -50 dB after normalization (see [member edit/normalize]). This prevents having files with silence at the beginning or end, which increases their size unnecessarily and adds latency to the moment they are played back. A fade-in/fade-out period of 500 samples is also used during trimming to avoid audible pops.
+ </member>
+ <member name="force/8_bit" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], forces the imported audio to use 8-bit quantization if the source file is 16-bit or higher.
+ Enabling this is generally not recommended, as 8-bit quantization decreases audio quality significantly. If you need smaller file sizes, consider using Ogg Vorbis or MP3 audio instead.
+ </member>
+ <member name="force/max_rate" type="bool" setter="" getter="" default="false">
+ If set to a value greater than [code]0[/code], forces the audio's sample rate to be reduced to a value lower than or equal to the value specified in [member force/max_rate_hz].
+ This can decrease file size noticeably on certain sounds, without impacting quality depending on the actual sound's contents. See [url=$DOCS_URL/tutorials/assets_pipeline/importing_audio_samples.html#doc-importing-audio-samples-best-practices]Best practices[/url] for more information.
+ </member>
+ <member name="force/max_rate_hz" type="float" setter="" getter="" default="44100">
+ The frequency to limit the imported audio sample to (in Hz). Only effective if [member force/max_rate] is [code]true[/code].
+ </member>
+ <member name="force/mono" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], forces the imported audio to be mono if the source file is stereo. This decreases the file size by 50% by merging the two channels into one.
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml
index a5fe56dc50..267594edcb 100644
--- a/doc/classes/ResourceLoader.xml
+++ b/doc/classes/ResourceLoader.xml
@@ -35,6 +35,12 @@
<param index="0" name="path" type="String" />
<description>
Returns the dependencies for the resource at the given [param path].
+ [b]Note:[/b] The dependencies are returned with slices separated by [code]::[/code]. You can use [method String.get_slice] to get their components.
+ [codeblock]
+ for dep in ResourceLoader.get_dependencies(path):
+ print(dep.get_slice("::", 0)) # Prints UID.
+ print(dep.get_slice("::", 2)) # Prints path.
+ [/codeblock]
</description>
</method>
<method name="get_recognized_extensions_for_type">
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 07a0b8438f..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" />
@@ -329,9 +347,10 @@
<method name="push_font">
<return type="void" />
<param index="0" name="font" type="Font" />
- <param index="1" name="font_size" type="int" />
+ <param index="1" name="font_size" type="int" default="0" />
<description>
Adds a [code][font][/code] tag to the tag stack. Overrides default fonts for its duration.
+ Passing [code]0[/code] to [param font_size] will use the existing default font size.
</description>
</method>
<method name="push_font_size">
@@ -506,7 +525,7 @@
<return type="void" />
<param index="0" name="column" type="int" />
<param index="1" name="expand" type="bool" />
- <param index="2" name="ratio" type="int" />
+ <param index="2" name="ratio" type="int" default="1" />
<description>
Edits the selected column's expansion options. If [param expand] is [code]true[/code], the column expands in proportion to its expansion ratio versus the other columns' ratios.
For example, 2 columns with ratios 3 and 4 plus 70 pixels in available width would expand 30 and 40 pixels, respectively.
diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml
index 2aa6707396..269ead1298 100644
--- a/doc/classes/RigidBody2D.xml
+++ b/doc/classes/RigidBody2D.xml
@@ -151,7 +151,7 @@
See [method add_constant_torque].
</member>
<member name="contact_monitor" type="bool" setter="set_contact_monitor" getter="is_contact_monitor_enabled" default="false">
- If [code]true[/code], the RigidBody2D will emit signals when it collides with another RigidBody2D.
+ If [code]true[/code], the RigidBody2D will emit signals when it collides with another body.
[b]Note:[/b] By default the maximum contacts reported is set to 0, meaning nothing will be recorded, see [member max_contacts_reported].
</member>
<member name="continuous_cd" type="int" setter="set_continuous_collision_detection_mode" getter="get_continuous_collision_detection_mode" enum="RigidBody2D.CCDMode" default="0">
diff --git a/doc/classes/RigidBody3D.xml b/doc/classes/RigidBody3D.xml
index 99d55d7c7f..69cfd2bb07 100644
--- a/doc/classes/RigidBody3D.xml
+++ b/doc/classes/RigidBody3D.xml
@@ -158,7 +158,7 @@
See [method add_constant_torque].
</member>
<member name="contact_monitor" type="bool" setter="set_contact_monitor" getter="is_contact_monitor_enabled" default="false">
- If [code]true[/code], the RigidBody3D will emit signals when it collides with another RigidBody3D.
+ If [code]true[/code], the RigidBody3D will emit signals when it collides with another body.
[b]Note:[/b] By default the maximum contacts reported is set to 0, meaning nothing will be recorded, see [member max_contacts_reported].
</member>
<member name="continuous_cd" type="bool" setter="set_use_continuous_collision_detection" getter="is_using_continuous_collision_detection" default="false">
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index 70c06eac09..c0d98ef921 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -42,7 +42,7 @@
<description>
Changes the running scene to the one at the given [param path], after loading it into a [PackedScene] and creating a new instance.
Returns [constant OK] on success, [constant ERR_CANT_OPEN] if the [param path] cannot be loaded into a [PackedScene], or [constant ERR_CANT_CREATE] if that scene cannot be instantiated.
- [b]Note:[/b] The scene change is deferred, which means that the new scene node is added to the tree at the end of the frame. This ensures that both scenes aren't running at the same time, while still freeing the previous scene in a safe way similar to [method Node.queue_free]. As such, you won't be able to access the loaded scene immediately after the [method change_scene_to_file] call.
+ [b]Note:[/b] The new scene node is added to the tree at the end of the frame. This ensures that both scenes aren't running at the same time, while still freeing the previous scene in a safe way similar to [method Node.queue_free]. As such, you won't be able to access the loaded scene immediately after the [method change_scene_to_file] call.
</description>
</method>
<method name="change_scene_to_packed">
@@ -51,7 +51,7 @@
<description>
Changes the running scene to a new instance of the given [PackedScene] (which must be valid).
Returns [constant OK] on success, [constant ERR_CANT_CREATE] if the scene cannot be instantiated, or [constant ERR_INVALID_PARAMETER] if the scene is invalid.
- [b]Note:[/b] The scene change is deferred, which means that the new scene node is added to the tree at the end of the frame. You won't be able to access it immediately after the [method change_scene_to_packed] call.
+ [b]Note:[/b] The new scene node is added to the tree at the end of the frame. You won't be able to access it immediately after the [method change_scene_to_packed] call.
</description>
</method>
<method name="create_timer">
diff --git a/doc/classes/SeparationRayShape3D.xml b/doc/classes/SeparationRayShape3D.xml
index 7d652c2527..aaf8791224 100644
--- a/doc/classes/SeparationRayShape3D.xml
+++ b/doc/classes/SeparationRayShape3D.xml
@@ -4,7 +4,7 @@
A 3D ray shape used for physics collision that tries to separate itself from any collider.
</brief_description>
<description>
- A 3D ray shape, intended for use in physics. Usually used to provide a shape for a [CollisionShape2D]. When a [SeparationRayShape3D] collides with an object, it tries to separate itself from it by moving its endpoint to the collision point. It can for example be used for spears falling from the sky.
+ A 3D ray shape, intended for use in physics. Usually used to provide a shape for a [CollisionShape3D]. When a [SeparationRayShape3D] collides with an object, it tries to separate itself from it by moving its endpoint to the collision point. It can for example be used for spears falling from the sky.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/ShaderMaterial.xml b/doc/classes/ShaderMaterial.xml
index 888964e7f0..e2d0696a4b 100644
--- a/doc/classes/ShaderMaterial.xml
+++ b/doc/classes/ShaderMaterial.xml
@@ -5,6 +5,7 @@
</brief_description>
<description>
A material that uses a custom [Shader] program to render either items to screen or process particles. You can create multiple materials for the same shader but configure different values for the uniforms defined in the shader.
+ [b]Note:[/b] For performance reasons the [signal Resource.changed] signal is only emitted when the [member Resource.resource_name] is changed. Only in editor, is also emitted for [member shader] changes.
</description>
<tutorials>
<link title="Shaders documentation index">$DOCS_URL/tutorials/shaders/index.html</link>
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/SliderJoint3D.xml b/doc/classes/SliderJoint3D.xml
index bdab8d7d78..49b362041b 100644
--- a/doc/classes/SliderJoint3D.xml
+++ b/doc/classes/SliderJoint3D.xml
@@ -105,7 +105,7 @@
A factor applied to the movement across the slider axis once the limits get surpassed. The lower, the slower the movement.
</constant>
<constant name="PARAM_LINEAR_LIMIT_RESTITUTION" value="3" enum="Param">
- The amount of restitution once the limits are surpassed. The lower, the more velocityenergy gets lost.
+ The amount of restitution once the limits are surpassed. The lower, the more velocity-energy gets lost.
</constant>
<constant name="PARAM_LINEAR_LIMIT_DAMPING" value="4" enum="Param">
The amount of damping once the slider limits are surpassed.
diff --git a/doc/classes/SubViewportContainer.xml b/doc/classes/SubViewportContainer.xml
index 2f666f5089..08e7ca23f7 100644
--- a/doc/classes/SubViewportContainer.xml
+++ b/doc/classes/SubViewportContainer.xml
@@ -11,6 +11,7 @@
<tutorials>
</tutorials>
<members>
+ <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="1" />
<member name="stretch" type="bool" setter="set_stretch" getter="is_stretch_enabled" default="false">
If [code]true[/code], the sub-viewport will be automatically resized to the control's size.
[b]Note:[/b] If [code]true[/code], this will prohibit changing [member SubViewport.size] of its children manually.
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 89ce894203..e611b7e3fa 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -1234,7 +1234,7 @@
<param index="1" name="to_line" type="int" />
<description>
Emitted immediately when the text changes.
- When text is added [param from_line] will be less then [param to_line]. On a remove [param to_line] will be less then [param from_line].
+ When text is added [param from_line] will be less than [param to_line]. On a remove [param to_line] will be less than [param from_line].
</description>
</signal>
<signal name="text_changed">
diff --git a/doc/classes/Texture2DArrayRD.xml b/doc/classes/Texture2DArrayRD.xml
new file mode 100644
index 0000000000..b28cdd436c
--- /dev/null
+++ b/doc/classes/Texture2DArrayRD.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Texture2DArrayRD" inherits="TextureLayeredRD" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Texture Array for 2D that is bound to a texture created on the [RenderingDevice].
+ </brief_description>
+ <description>
+ This texture array class allows you to use a 2D array texture created directly on the [RenderingDevice] as a texture for materials, meshes, etc.
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/doc/classes/Texture2DRD.xml b/doc/classes/Texture2DRD.xml
new file mode 100644
index 0000000000..b935a7763b
--- /dev/null
+++ b/doc/classes/Texture2DRD.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Texture2DRD" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Texture for 2D that is bound to a texture created on the [RenderingDevice].
+ </brief_description>
+ <description>
+ This texture class allows you to use a 2D texture created directly on the [RenderingDevice] as a texture for materials, meshes, etc.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" />
+ <member name="texture_rd_rid" type="RID" setter="set_texture_rd_rid" getter="get_texture_rd_rid" default="RID()">
+ The RID of the texture object created on the [RenderingDevice].
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/Texture3DRD.xml b/doc/classes/Texture3DRD.xml
new file mode 100644
index 0000000000..f9d72b7a0f
--- /dev/null
+++ b/doc/classes/Texture3DRD.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Texture3DRD" inherits="Texture3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Texture for 3D that is bound to a texture created on the [RenderingDevice].
+ </brief_description>
+ <description>
+ This texture class allows you to use a 3D texture created directly on the [RenderingDevice] as a texture for materials, meshes, etc.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="texture_rd_rid" type="RID" setter="set_texture_rd_rid" getter="get_texture_rd_rid" default="RID()">
+ The RID of the texture object created on the [RenderingDevice].
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/TextureCubemapArrayRD.xml b/doc/classes/TextureCubemapArrayRD.xml
new file mode 100644
index 0000000000..38d5010ee7
--- /dev/null
+++ b/doc/classes/TextureCubemapArrayRD.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TextureCubemapArrayRD" inherits="TextureLayeredRD" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Texture Array for Cubemaps that is bound to a texture created on the [RenderingDevice].
+ </brief_description>
+ <description>
+ This texture class allows you to use a cubemap array texture created directly on the [RenderingDevice] as a texture for materials, meshes, etc.
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/doc/classes/TextureCubemapRD.xml b/doc/classes/TextureCubemapRD.xml
new file mode 100644
index 0000000000..bd7c897ad3
--- /dev/null
+++ b/doc/classes/TextureCubemapRD.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TextureCubemapRD" inherits="TextureLayeredRD" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Texture for Cubemap that is bound to a texture created on the [RenderingDevice].
+ </brief_description>
+ <description>
+ This texture class allows you to use a cubemap texture created directly on the [RenderingDevice] as a texture for materials, meshes, etc.
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/doc/classes/TextureLayeredRD.xml b/doc/classes/TextureLayeredRD.xml
new file mode 100644
index 0000000000..65f2d87624
--- /dev/null
+++ b/doc/classes/TextureLayeredRD.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TextureLayeredRD" inherits="TextureLayered" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Abstract base class for layered texture RD types.
+ </brief_description>
+ <description>
+ Base class for [Texture2DArrayRD], [TextureCubemapRD] and [TextureCubemapArrayRD]. Cannot be used directly, but contains all the functions necessary for accessing the derived resource types.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="texture_rd_rid" type="RID" setter="set_texture_rd_rid" getter="get_texture_rd_rid" default="RID()">
+ The RID of the texture object created on the [RenderingDevice].
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml
index 068f296c98..dbf63c0852 100644
--- a/doc/classes/Thread.xml
+++ b/doc/classes/Thread.xml
@@ -5,7 +5,6 @@
</brief_description>
<description>
A unit of execution in a process. Can run methods on [Object]s simultaneously. The use of synchronization via [Mutex] or [Semaphore] is advised if working with shared objects.
- [b]Note:[/b] Breakpoints won't break on code if it's running in a thread. This is a current limitation of the GDScript debugger.
[b]Warning:[/b]
To ensure proper cleanup without crashes or deadlocks, when a [Thread]'s reference count reaches zero and it is therefore destroyed, the following conditions must be met:
- It must not have any [Mutex] objects locked.
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/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml
index c72b638fc3..7daabcbfee 100644
--- a/doc/classes/TileSetAtlasSource.xml
+++ b/doc/classes/TileSetAtlasSource.xml
@@ -80,6 +80,13 @@
Returns how many animation frames has the tile at coordinates [param atlas_coords].
</description>
</method>
+ <method name="get_tile_animation_mode" qualifiers="const">
+ <return type="int" enum="TileSetAtlasSource.TileAnimationMode" />
+ <param index="0" name="atlas_coords" type="Vector2i" />
+ <description>
+ Returns the [enum TileAnimationMode] of the tile at [param atlas_coords]. See also [method set_tile_animation_mode].
+ </description>
+ </method>
<method name="get_tile_animation_separation" qualifiers="const">
<return type="Vector2i" />
<param index="0" name="atlas_coords" type="Vector2i" />
@@ -215,6 +222,14 @@
Sets how many animation frames the tile at coordinates [param atlas_coords] has.
</description>
</method>
+ <method name="set_tile_animation_mode">
+ <return type="void" />
+ <param index="0" name="atlas_coords" type="Vector2i" />
+ <param index="1" name="mode" type="int" enum="TileSetAtlasSource.TileAnimationMode" />
+ <description>
+ Sets the [enum TileAnimationMode] of the tile at [param atlas_coords] to [param mode]. See also [method get_tile_animation_mode].
+ </description>
+ </method>
<method name="set_tile_animation_separation">
<return type="void" />
<param index="0" name="atlas_coords" type="Vector2i" />
@@ -250,4 +265,15 @@
Disabling this setting might lead a small performance improvement, as generating the internal texture requires both memory and processing time when the TileSetAtlasSource resource is modified.
</member>
</members>
+ <constants>
+ <constant name="TILE_ANIMATION_MODE_DEFAULT" value="0" enum="TileAnimationMode">
+ Tile animations start at same time, looking identical.
+ </constant>
+ <constant name="TILE_ANIMATION_MODE_RANDOM_START_TIMES" value="1" enum="TileAnimationMode">
+ Tile animations start at random times, looking varied.
+ </constant>
+ <constant name="TILE_ANIMATION_MODE_MAX" value="2" enum="TileAnimationMode">
+ Represents the size of the [enum TileAnimationMode] enum.
+ </constant>
+ </constants>
</class>
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 6f688c330c..a1932ba5b6 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -542,6 +542,18 @@
<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
The maximum allowed width of the icon in item's cells. This limit is applied on top of the default size of the icon, but before the value set with [method TreeItem.set_icon_max_width]. The height is adjusted according to the icon's ratio.
</theme_item>
+ <theme_item name="inner_item_margin_bottom" data_type="constant" type="int" default="0">
+ The inner bottom margin of an item.
+ </theme_item>
+ <theme_item name="inner_item_margin_left" data_type="constant" type="int" default="0">
+ The inner left margin of an item.
+ </theme_item>
+ <theme_item name="inner_item_margin_right" data_type="constant" type="int" default="0">
+ The inner right margin of an item.
+ </theme_item>
+ <theme_item name="inner_item_margin_top" data_type="constant" type="int" default="0">
+ The inner top margin of an item.
+ </theme_item>
<theme_item name="item_margin" data_type="constant" type="int" default="16">
The horizontal margin at the start of an item. This is used when folding is enabled for the item.
</theme_item>
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index 8925280291..865571263e 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -490,6 +490,15 @@
If [code]true[/code], disables the button at index [param button_index] in the given [param column].
</description>
</method>
+ <method name="set_button_tooltip_text">
+ <return type="void" />
+ <param index="0" name="column" type="int" />
+ <param index="1" name="button_index" type="int" />
+ <param index="2" name="tooltip" type="String" />
+ <description>
+ Sets the tooltip text for the button at index [param button_index] in the given [param column].
+ </description>
+ </method>
<method name="set_cell_mode">
<return type="void" />
<param index="0" name="column" type="int" />
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index fd8ef507fb..7645b249e3 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -206,6 +206,7 @@
<return type="void" />
<description>
Pauses the tweening. The animation can be resumed by using [method play].
+ [b]Note:[/b] If a Tween is paused and not bound to any node, it will exist indefinitely until manually started or invalidated. If you lose a reference to such Tween, you can retrieve it using [method SceneTree.get_processed_tweens].
</description>
</method>
<method name="play">
@@ -273,6 +274,7 @@
<return type="void" />
<description>
Stops the tweening and resets the [Tween] to its initial state. This will not remove any appended [Tweener]s.
+ [b]Note:[/b] If a Tween is stopped and not bound to any node, it will exist indefinitely until manually started or invalidated. If you lose a reference to such Tween, you can retrieve it using [method SceneTree.get_processed_tweens].
</description>
</method>
<method name="tween_callback">
@@ -364,7 +366,7 @@
[/gdscript]
[csharp]
Tween tween = CreateTween();
- tween.TweenMethod(Callable.From(() =&gt; LookAt(Vector3.Up)), new Vector3(-1.0f, 0.0f, -1.0f), new Vector3(1.0f, 0.0f, -1.0f), 1.0f); // The LookAt() method takes up vector as second argument.
+ tween.TweenMethod(Callable.From((Vector3 target) =&gt; LookAt(target, Vector3.Up)), new Vector3(-1.0f, 0.0f, -1.0f), new Vector3(1.0f, 0.0f, -1.0f), 1.0f); // Use lambdas to bind additional arguments for the call.
[/csharp]
[/codeblocks]
[b]Example:[/b] Setting the text of a [Label], using an intermediate method and after a delay:
diff --git a/doc/classes/VideoStreamPlayer.xml b/doc/classes/VideoStreamPlayer.xml
index 41f49a5a91..19b1e20e5b 100644
--- a/doc/classes/VideoStreamPlayer.xml
+++ b/doc/classes/VideoStreamPlayer.xml
@@ -12,6 +12,13 @@
<tutorials>
</tutorials>
<methods>
+ <method name="get_stream_length" qualifiers="const">
+ <return type="float" />
+ <description>
+ The length of the current stream, in seconds.
+ [b]Note:[/b] For [VideoStreamTheora] streams (the built-in format supported by Godot), this value will always be zero, as getting the stream length is not implemented yet. The feature may be supported by video formats implemented by a GDExtension add-on.
+ </description>
+ </method>
<method name="get_stream_name" qualifiers="const">
<return type="String" />
<description>
@@ -61,6 +68,9 @@
<member name="expand" type="bool" setter="set_expand" getter="has_expand" default="false">
If [code]true[/code], the video scales to the control size. Otherwise, the control minimum size will be automatically adjusted to match the video stream's dimensions.
</member>
+ <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false">
+ If [code]true[/code], the video restarts when it reaches its end.
+ </member>
<member name="paused" type="bool" setter="set_paused" getter="is_paused" default="false">
If [code]true[/code], the video is paused.
</member>
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index c0c53146fe..9a5e7ed6f6 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -160,8 +160,8 @@
- [method Node._input]
- [method Control._gui_input] for [Control] nodes
- [method Node._shortcut_input]
- - [method Node._unhandled_input]
- [method Node._unhandled_key_input]
+ - [method Node._unhandled_input]
If an earlier method marks the input as handled via [method set_input_as_handled], any later method in this list will not be called.
If none of the methods handle the event and [member physics_object_picking] is [code]true[/code], the event is used for physics object picking.
</description>
@@ -183,8 +183,8 @@
While this method serves a similar purpose as [method Input.parse_input_event], it does not remap the specified [param event] based on project settings like [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse].
Calling this method will propagate calls to child nodes for following methods in the given order:
- [method Node._shortcut_input]
- - [method Node._unhandled_input]
- [method Node._unhandled_key_input]
+ - [method Node._unhandled_input]
If an earlier method marks the input as handled via [method set_input_as_handled], any later method in this list will not be called.
If none of the methods handle the event and [member physics_object_picking] is [code]true[/code], the event is used for physics object picking.
[b]Note:[/b] This method doesn't propagate input events to embedded [Window]s or [SubViewport]s.
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/VisualShaderNodeRotationByAxis.xml b/doc/classes/VisualShaderNodeRotationByAxis.xml
new file mode 100644
index 0000000000..8a4fbc89d8
--- /dev/null
+++ b/doc/classes/VisualShaderNodeRotationByAxis.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeRotationByAxis" inherits="VisualShaderNode" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ A visual shader node that modifies the rotation of the object using a rotation matrix.
+ </brief_description>
+ <description>
+ RotationByAxis node will transform the vertices of a mesh with specified axis and angle in radians. It can be used to rotate an object in an arbitrary axis.
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/doc/classes/VisualShaderNodeScreenNormalWorldSpace.xml b/doc/classes/VisualShaderNodeScreenNormalWorldSpace.xml
new file mode 100644
index 0000000000..ccffb62138
--- /dev/null
+++ b/doc/classes/VisualShaderNodeScreenNormalWorldSpace.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeScreenNormalWorldSpace" inherits="VisualShaderNode" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ A visual shader node that unpacks the screen normal texture in World Space.
+ </brief_description>
+ <description>
+ The ScreenNormalWorldSpace node allows to create outline effects.
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/doc/classes/VisualShaderNodeWorldPositionFromDepth.xml b/doc/classes/VisualShaderNodeWorldPositionFromDepth.xml
new file mode 100644
index 0000000000..9c9016e889
--- /dev/null
+++ b/doc/classes/VisualShaderNodeWorldPositionFromDepth.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeWorldPositionFromDepth" inherits="VisualShaderNode" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ A visual shader node that calculates the position of the pixel in world space using the depth texture.
+ </brief_description>
+ <description>
+ The WorldPositionFromDepth node reconstructs the depth position of the pixel in world space. This can be used to obtain world space UVs for projection mapping like Caustics.
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 0446f6d73f..33a64be50c 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -719,12 +719,12 @@
</signal>
<signal name="mouse_entered">
<description>
- Emitted when the mouse cursor enters the [Window]'s area, regardless if it's currently focused or not.
+ Emitted when the mouse cursor enters the [Window]'s visible area, that is not occluded behind other [Control]s or windows, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not.
</description>
</signal>
<signal name="mouse_exited">
<description>
- Emitted when the mouse cursor exits the [Window]'s area (including when it's hovered over another window on top of this one).
+ Emitted when the mouse cursor leaves the [Window]'s visible area, that is not occluded behind other [Control]s or windows, provided its [member Viewport.gui_disable_input] is [code]false[/code] and regardless if it's currently focused or not.
</description>
</signal>
<signal name="theme_changed">
@@ -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/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp
index b8c56018dc..658c0e6145 100644
--- a/drivers/gles3/effects/copy_effects.cpp
+++ b/drivers/gles3/effects/copy_effects.cpp
@@ -31,6 +31,7 @@
#ifdef GLES3_ENABLED
#include "copy_effects.h"
+#include "../storage/texture_storage.h"
using namespace GLES3;
@@ -133,6 +134,7 @@ void CopyEffects::copy_screen() {
draw_screen_triangle();
}
+// Intended for efficiently mipmapping textures.
void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region) {
GLuint framebuffers[2];
glGenFramebuffers(2, framebuffers);
@@ -158,6 +160,80 @@ void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, con
glDeleteFramebuffers(2, framebuffers);
}
+// Intended for approximating a gaussian blur. Used for 2D backbuffer mipmaps. Slightly less efficient than bilinear_blur().
+void CopyEffects::gaussian_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region, const Size2i &p_size) {
+ GLuint framebuffer;
+ glGenFramebuffers(1, &framebuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, p_source_texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ Size2i base_size = p_size;
+
+ Rect2i source_region = p_region;
+ Rect2i dest_region = p_region;
+
+ Size2 float_size = Size2(p_size);
+ Rect2 normalized_source_region = Rect2(p_region);
+ normalized_source_region.position = normalized_source_region.position / float_size;
+ normalized_source_region.size = normalized_source_region.size / float_size;
+ Rect2 normalized_dest_region = Rect2(p_region);
+ for (int i = 1; i < p_mipmap_count; i++) {
+ dest_region.position.x >>= 1;
+ dest_region.position.y >>= 1;
+ dest_region.size.x = MAX(1, dest_region.size.x >> 1);
+ dest_region.size.y = MAX(1, dest_region.size.y >> 1);
+ base_size.x >>= 1;
+ base_size.y >>= 1;
+
+ glBindTexture(GL_TEXTURE_2D, p_source_texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, i - 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i - 1);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, i);
+#ifdef DEV_ENABLED
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ WARN_PRINT("Could not bind Gaussian blur framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
+ }
+#endif
+
+ glViewport(0, 0, base_size.x, base_size.y);
+
+ bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
+ if (!success) {
+ return;
+ }
+
+ float_size = Size2(base_size);
+ normalized_dest_region.position = Size2(dest_region.position) / float_size;
+ normalized_dest_region.size = Size2(dest_region.size) / float_size;
+
+ copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, normalized_dest_region.position.x, normalized_dest_region.position.y, normalized_dest_region.size.x, normalized_dest_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
+ copy.shader.version_set_uniform(CopyShaderGLES3::SOURCE_SECTION, normalized_source_region.position.x, normalized_source_region.position.y, normalized_source_region.size.x, normalized_source_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
+ copy.shader.version_set_uniform(CopyShaderGLES3::PIXEL_SIZE, 1.0 / float_size.x, 1.0 / float_size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
+
+ draw_screen_quad();
+
+ source_region = dest_region;
+ normalized_source_region = normalized_dest_region;
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glDeleteFramebuffers(1, &framebuffer);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, p_mipmap_count - 1);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glViewport(0, 0, p_size.x, p_size.y);
+}
+
void CopyEffects::set_color(const Color &p_color, const Rect2i &p_region) {
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
if (!success) {
diff --git a/drivers/gles3/effects/copy_effects.h b/drivers/gles3/effects/copy_effects.h
index 105fbcfe98..b2bceb84af 100644
--- a/drivers/gles3/effects/copy_effects.h
+++ b/drivers/gles3/effects/copy_effects.h
@@ -64,6 +64,7 @@ public:
void copy_to_rect(const Rect2 &p_rect);
void copy_screen();
void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region);
+ void gaussian_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region, const Size2i &p_size);
void set_color(const Color &p_color, const Rect2i &p_region);
void draw_screen_triangle();
void draw_screen_quad();
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index e6e7dd6f17..1fe33b7914 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -608,18 +608,20 @@ void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, cons
material = nullptr;
}
}
+ }
- if (!material) {
- sky_material = sky_globals.default_material;
- material = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY));
- }
+ if (!material) {
+ sky_material = sky_globals.default_material;
+ material = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY));
+ }
- ERR_FAIL_COND(!material);
+ ERR_FAIL_COND(!material);
- shader_data = material->shader_data;
+ shader_data = material->shader_data;
- ERR_FAIL_COND(!shader_data);
+ ERR_FAIL_COND(!shader_data);
+ if (sky) {
if (shader_data->uses_time && time - sky->prev_time > 0.00001) {
sky->prev_time = time;
sky->reflection_dirty = true;
@@ -640,111 +642,113 @@ void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, cons
sky->prev_position = p_transform.origin;
sky->reflection_dirty = true;
}
+ }
- glBindBufferBase(GL_UNIFORM_BUFFER, SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, sky_globals.directional_light_buffer);
- if (shader_data->uses_light) {
- sky_globals.directional_light_count = 0;
- for (int i = 0; i < (int)p_lights.size(); i++) {
- GLES3::LightInstance *li = GLES3::LightStorage::get_singleton()->get_light_instance(p_lights[i]);
- if (!li) {
- continue;
- }
- RID base = li->light;
+ glBindBufferBase(GL_UNIFORM_BUFFER, SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, sky_globals.directional_light_buffer);
+ if (shader_data->uses_light) {
+ sky_globals.directional_light_count = 0;
+ for (int i = 0; i < (int)p_lights.size(); i++) {
+ GLES3::LightInstance *li = GLES3::LightStorage::get_singleton()->get_light_instance(p_lights[i]);
+ if (!li) {
+ continue;
+ }
+ RID base = li->light;
- ERR_CONTINUE(base.is_null());
+ ERR_CONTINUE(base.is_null());
- RS::LightType type = light_storage->light_get_type(base);
- if (type == RS::LIGHT_DIRECTIONAL && light_storage->light_directional_get_sky_mode(base) != RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_ONLY) {
- DirectionalLightData &sky_light_data = sky_globals.directional_lights[sky_globals.directional_light_count];
- Transform3D light_transform = li->transform;
- Vector3 world_direction = light_transform.basis.xform(Vector3(0, 0, 1)).normalized();
+ RS::LightType type = light_storage->light_get_type(base);
+ if (type == RS::LIGHT_DIRECTIONAL && light_storage->light_directional_get_sky_mode(base) != RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_ONLY) {
+ DirectionalLightData &sky_light_data = sky_globals.directional_lights[sky_globals.directional_light_count];
+ Transform3D light_transform = li->transform;
+ Vector3 world_direction = light_transform.basis.xform(Vector3(0, 0, 1)).normalized();
- sky_light_data.direction[0] = world_direction.x;
- sky_light_data.direction[1] = world_direction.y;
- sky_light_data.direction[2] = world_direction.z;
+ sky_light_data.direction[0] = world_direction.x;
+ sky_light_data.direction[1] = world_direction.y;
+ sky_light_data.direction[2] = world_direction.z;
- float sign = light_storage->light_is_negative(base) ? -1 : 1;
- sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY);
+ float sign = light_storage->light_is_negative(base) ? -1 : 1;
+ sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY);
- if (is_using_physical_light_units()) {
- sky_light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY);
- }
+ if (is_using_physical_light_units()) {
+ sky_light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY);
+ }
- if (p_render_data->camera_attributes.is_valid()) {
- sky_light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes);
- }
+ if (p_render_data->camera_attributes.is_valid()) {
+ sky_light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes);
+ }
- Color linear_col = light_storage->light_get_color(base);
- sky_light_data.color[0] = linear_col.r;
- sky_light_data.color[1] = linear_col.g;
- sky_light_data.color[2] = linear_col.b;
+ Color linear_col = light_storage->light_get_color(base);
+ sky_light_data.color[0] = linear_col.r;
+ sky_light_data.color[1] = linear_col.g;
+ sky_light_data.color[2] = linear_col.b;
- sky_light_data.enabled = true;
+ sky_light_data.enabled = true;
- float angular_diameter = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE);
- if (angular_diameter > 0.0) {
- angular_diameter = Math::tan(Math::deg_to_rad(angular_diameter));
- } else {
- angular_diameter = 0.0;
- }
- sky_light_data.size = angular_diameter;
- sky_globals.directional_light_count++;
- if (sky_globals.directional_light_count >= sky_globals.max_directional_lights) {
- break;
- }
+ float angular_diameter = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE);
+ if (angular_diameter > 0.0) {
+ angular_diameter = Math::tan(Math::deg_to_rad(angular_diameter));
+ } else {
+ angular_diameter = 0.0;
}
- }
- // Check whether the directional_light_buffer changes
- bool light_data_dirty = false;
-
- // Light buffer is dirty if we have fewer or more lights
- // If we have fewer lights, make sure that old lights are disabled
- if (sky_globals.directional_light_count != sky_globals.last_frame_directional_light_count) {
- light_data_dirty = true;
- for (uint32_t i = sky_globals.directional_light_count; i < sky_globals.max_directional_lights; i++) {
- sky_globals.directional_lights[i].enabled = false;
- sky_globals.last_frame_directional_lights[i].enabled = false;
+ sky_light_data.size = angular_diameter;
+ sky_globals.directional_light_count++;
+ if (sky_globals.directional_light_count >= sky_globals.max_directional_lights) {
+ break;
}
}
+ }
+ // Check whether the directional_light_buffer changes
+ bool light_data_dirty = false;
+
+ // Light buffer is dirty if we have fewer or more lights
+ // If we have fewer lights, make sure that old lights are disabled
+ if (sky_globals.directional_light_count != sky_globals.last_frame_directional_light_count) {
+ light_data_dirty = true;
+ for (uint32_t i = sky_globals.directional_light_count; i < sky_globals.max_directional_lights; i++) {
+ sky_globals.directional_lights[i].enabled = false;
+ sky_globals.last_frame_directional_lights[i].enabled = false;
+ }
+ }
- if (!light_data_dirty) {
- for (uint32_t i = 0; i < sky_globals.directional_light_count; i++) {
- if (sky_globals.directional_lights[i].direction[0] != sky_globals.last_frame_directional_lights[i].direction[0] ||
- sky_globals.directional_lights[i].direction[1] != sky_globals.last_frame_directional_lights[i].direction[1] ||
- sky_globals.directional_lights[i].direction[2] != sky_globals.last_frame_directional_lights[i].direction[2] ||
- sky_globals.directional_lights[i].energy != sky_globals.last_frame_directional_lights[i].energy ||
- sky_globals.directional_lights[i].color[0] != sky_globals.last_frame_directional_lights[i].color[0] ||
- sky_globals.directional_lights[i].color[1] != sky_globals.last_frame_directional_lights[i].color[1] ||
- sky_globals.directional_lights[i].color[2] != sky_globals.last_frame_directional_lights[i].color[2] ||
- sky_globals.directional_lights[i].enabled != sky_globals.last_frame_directional_lights[i].enabled ||
- sky_globals.directional_lights[i].size != sky_globals.last_frame_directional_lights[i].size) {
- light_data_dirty = true;
- break;
- }
+ if (!light_data_dirty) {
+ for (uint32_t i = 0; i < sky_globals.directional_light_count; i++) {
+ if (sky_globals.directional_lights[i].direction[0] != sky_globals.last_frame_directional_lights[i].direction[0] ||
+ sky_globals.directional_lights[i].direction[1] != sky_globals.last_frame_directional_lights[i].direction[1] ||
+ sky_globals.directional_lights[i].direction[2] != sky_globals.last_frame_directional_lights[i].direction[2] ||
+ sky_globals.directional_lights[i].energy != sky_globals.last_frame_directional_lights[i].energy ||
+ sky_globals.directional_lights[i].color[0] != sky_globals.last_frame_directional_lights[i].color[0] ||
+ sky_globals.directional_lights[i].color[1] != sky_globals.last_frame_directional_lights[i].color[1] ||
+ sky_globals.directional_lights[i].color[2] != sky_globals.last_frame_directional_lights[i].color[2] ||
+ sky_globals.directional_lights[i].enabled != sky_globals.last_frame_directional_lights[i].enabled ||
+ sky_globals.directional_lights[i].size != sky_globals.last_frame_directional_lights[i].size) {
+ light_data_dirty = true;
+ break;
}
}
+ }
- if (light_data_dirty) {
- glBufferData(GL_UNIFORM_BUFFER, sizeof(DirectionalLightData) * sky_globals.max_directional_lights, sky_globals.directional_lights, GL_STREAM_DRAW);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ if (light_data_dirty) {
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(DirectionalLightData) * sky_globals.max_directional_lights, sky_globals.directional_lights, GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
- DirectionalLightData *temp = sky_globals.last_frame_directional_lights;
- sky_globals.last_frame_directional_lights = sky_globals.directional_lights;
- sky_globals.directional_lights = temp;
- sky_globals.last_frame_directional_light_count = sky_globals.directional_light_count;
+ DirectionalLightData *temp = sky_globals.last_frame_directional_lights;
+ sky_globals.last_frame_directional_lights = sky_globals.directional_lights;
+ sky_globals.directional_lights = temp;
+ sky_globals.last_frame_directional_light_count = sky_globals.directional_light_count;
+ if (sky) {
sky->reflection_dirty = true;
}
}
+ }
- if (p_render_data->view_count > 1) {
- glBindBufferBase(GL_UNIFORM_BUFFER, SKY_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
- }
+ if (p_render_data->view_count > 1) {
+ glBindBufferBase(GL_UNIFORM_BUFFER, SKY_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ }
- if (!sky->radiance) {
- _invalidate_sky(sky);
- _update_dirty_skys();
- }
+ if (sky && !sky->radiance) {
+ _invalidate_sky(sky);
+ _update_dirty_skys();
}
}
@@ -1734,7 +1738,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
RenderDataGLES3 render_data;
{
render_data.render_buffers = rb;
- render_data.transparent_bg = rb.is_valid() ? rb->is_transparent : false;
+ render_data.transparent_bg = rb.is_valid() ? rt->is_transparent : false;
// Our first camera is used by default
render_data.cam_transform = p_camera_data->main_transform;
render_data.inv_cam_transform = render_data.cam_transform.affine_inverse();
@@ -1980,6 +1984,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
}
if (!keep_color) {
+ clear_color.a = render_data.transparent_bg ? 0.0f : 1.0f;
glClearBufferfv(GL_COLOR, 0, clear_color.components);
}
RENDER_TIMESTAMP("Render Opaque Pass");
@@ -2166,7 +2171,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
if (scene_state.current_depth_draw != shader->depth_draw) {
switch (shader->depth_draw) {
case GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE: {
- glDepthMask(p_pass_mode == PASS_MODE_COLOR);
+ glDepthMask((p_pass_mode == PASS_MODE_COLOR && !GLES3::Config::get_singleton()->use_depth_prepass) ||
+ p_pass_mode == PASS_MODE_DEPTH ||
+ p_pass_mode == PASS_MODE_SHADOW);
} break;
case GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS: {
glDepthMask(GL_TRUE);
diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp
index 29e9ada72b..fdc787b4a1 100644
--- a/drivers/gles3/shader_gles3.cpp
+++ b/drivers/gles3/shader_gles3.cpp
@@ -667,7 +667,7 @@ void ShaderGLES3::_clear_version(Version *p_version) {
void ShaderGLES3::_initialize_version(Version *p_version) {
ERR_FAIL_COND(p_version->variants.size() > 0);
- if (_load_from_cache(p_version)) {
+ if (shader_cache_dir_valid && _load_from_cache(p_version)) {
return;
}
p_version->variants.reserve(variant_count);
@@ -678,7 +678,9 @@ void ShaderGLES3::_initialize_version(Version *p_version) {
_compile_specialization(spec, i, p_version, specialization_default_mask);
p_version->variants[i].insert(specialization_default_mask, spec);
}
- _save_to_cache(p_version);
+ if (shader_cache_dir_valid) {
+ _save_to_cache(p_version);
+ }
}
void ShaderGLES3::version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const LocalVector<ShaderGLES3::TextureUniformData> &p_texture_uniforms, bool p_initialize) {
diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl
index 796ba79c2e..3a85569555 100644
--- a/drivers/gles3/shaders/copy.glsl
+++ b/drivers/gles3/shaders/copy.glsl
@@ -16,17 +16,24 @@ layout(location = 0) in vec2 vertex_attrib;
out vec2 uv_interp;
/* clang-format on */
-#ifdef USE_COPY_SECTION
+#if defined(USE_COPY_SECTION) || defined(MODE_GAUSSIAN_BLUR)
+// Defined in 0-1 coords.
uniform highp vec4 copy_section;
#endif
+#ifdef MODE_GAUSSIAN_BLUR
+uniform highp vec4 source_section;
+#endif
void main() {
uv_interp = vertex_attrib * 0.5 + 0.5;
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
-#ifdef USE_COPY_SECTION
+#if defined(USE_COPY_SECTION) || defined(MODE_GAUSSIAN_BLUR)
gl_Position.xy = (copy_section.xy + uv_interp.xy * copy_section.zw) * 2.0 - 1.0;
#endif
+#ifdef MODE_GAUSSIAN_BLUR
+ uv_interp = source_section.xy + uv_interp * source_section.zw;
+#endif
}
/* clang-format off */
@@ -39,6 +46,7 @@ uniform vec4 color_in;
#endif
#ifdef MODE_GAUSSIAN_BLUR
+// Defined in 0-1 coords.
uniform highp vec2 pixel_size;
#endif
@@ -55,4 +63,31 @@ void main() {
#ifdef MODE_SIMPLE_COLOR
frag_color = color_in;
#endif
+
+// Efficient box filter from Jimenez: http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
+// Approximates a Gaussian in a single pass.
+#ifdef MODE_GAUSSIAN_BLUR
+ vec4 A = textureLod(source, uv_interp + pixel_size * vec2(-1.0, -1.0), 0.0);
+ vec4 B = textureLod(source, uv_interp + pixel_size * vec2(0.0, -1.0), 0.0);
+ vec4 C = textureLod(source, uv_interp + pixel_size * vec2(1.0, -1.0), 0.0);
+ vec4 D = textureLod(source, uv_interp + pixel_size * vec2(-0.5, -0.5), 0.0);
+ vec4 E = textureLod(source, uv_interp + pixel_size * vec2(0.5, -0.5), 0.0);
+ vec4 F = textureLod(source, uv_interp + pixel_size * vec2(-1.0, 0.0), 0.0);
+ vec4 G = textureLod(source, uv_interp, 0.0);
+ vec4 H = textureLod(source, uv_interp + pixel_size * vec2(1.0, 0.0), 0.0);
+ vec4 I = textureLod(source, uv_interp + pixel_size * vec2(-0.5, 0.5), 0.0);
+ vec4 J = textureLod(source, uv_interp + pixel_size * vec2(0.5, 0.5), 0.0);
+ vec4 K = textureLod(source, uv_interp + pixel_size * vec2(-1.0, 1.0), 0.0);
+ vec4 L = textureLod(source, uv_interp + pixel_size * vec2(0.0, 1.0), 0.0);
+ vec4 M = textureLod(source, uv_interp + pixel_size * vec2(1.0, 1.0), 0.0);
+
+ float weight = 0.5 / 4.0;
+ float lesser_weight = 0.125 / 4.0;
+
+ frag_color = (D + E + I + J) * weight;
+ frag_color += (A + B + G + F) * lesser_weight;
+ frag_color += (B + C + H + G) * lesser_weight;
+ frag_color += (F + G + L + K) * lesser_weight;
+ frag_color += (G + H + M + L) * lesser_weight;
+#endif
}
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index aa68febec8..7cba77be2f 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -1039,17 +1039,16 @@ void main() {
if (alpha < alpha_scissor_threshold) {
discard;
}
-#endif // ALPHA_SCISSOR_USED
-
+#else
+#ifdef MODE_RENDER_DEPTH
#ifdef USE_OPAQUE_PREPASS
-#if !defined(ALPHA_SCISSOR_USED)
if (alpha < opaque_prepass_threshold) {
discard;
}
-
-#endif // not ALPHA_SCISSOR_USED
#endif // USE_OPAQUE_PREPASS
+#endif // MODE_RENDER_DEPTH
+#endif // !ALPHA_SCISSOR_USED
#endif // !USE_SHADOW_TO_OPACITY
@@ -1270,17 +1269,16 @@ void main() {
if (alpha < alpha_scissor) {
discard;
}
-#endif // ALPHA_SCISSOR_USED
-
+#else
+#ifdef MODE_RENDER_DEPTH
#ifdef USE_OPAQUE_PREPASS
-#if !defined(ALPHA_SCISSOR_USED)
if (alpha < opaque_prepass_threshold) {
discard;
}
-
-#endif // not ALPHA_SCISSOR_USED
#endif // USE_OPAQUE_PREPASS
+#endif // MODE_RENDER_DEPTH
+#endif // !ALPHA_SCISSOR_USED
#endif // USE_SHADOW_TO_OPACITY
diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl
index 191d873269..9f9c22cf6d 100644
--- a/drivers/gles3/shaders/sky.glsl
+++ b/drivers/gles3/shaders/sky.glsl
@@ -167,24 +167,24 @@ void main() {
#ifdef USE_CUBEMAP_PASS
#ifdef USES_HALF_RES_COLOR
- half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal);
+ half_res_color = texture(samplerCube(half_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal);
#endif
#ifdef USES_QUARTER_RES_COLOR
- quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal);
+ quarter_res_color = texture(samplerCube(quarter_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal);
#endif
#else
#ifdef USES_HALF_RES_COLOR
#ifdef USE_MULTIVIEW
- half_res_color = textureLod(sampler2DArray(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(uv, ViewIndex), 0.0);
+ half_res_color = textureLod(sampler2DArray(half_res, SAMPLER_LINEAR_CLAMP), vec3(uv, ViewIndex), 0.0);
#else
- half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0);
+ half_res_color = textureLod(sampler2D(half_res, SAMPLER_LINEAR_CLAMP), uv, 0.0);
#endif
#endif
#ifdef USES_QUARTER_RES_COLOR
#ifdef USE_MULTIVIEW
- quarter_res_color = textureLod(sampler2DArray(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(uv, ViewIndex), 0.0);
+ quarter_res_color = textureLod(sampler2DArray(quarter_res, SAMPLER_LINEAR_CLAMP), vec3(uv, ViewIndex), 0.0);
#else
- quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0);
+ quarter_res_color = textureLod(sampler2D(quarter_res, SAMPLER_LINEAR_CLAMP), uv, 0.0);
#endif
#endif
#endif
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index d0746a8fc8..aa6319f0ef 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1338,7 +1338,7 @@ MaterialStorage::MaterialStorage() {
actions.render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n";
actions.render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n";
actions.render_mode_defines["particle_trails"] = "#define USE_PARTICLE_TRAILS\n";
- actions.render_mode_defines["depth_draw_opaque"] = "#define USE_OPAQUE_PREPASS\n";
+ actions.render_mode_defines["depth_prepass_alpha"] = "#define USE_OPAQUE_PREPASS\n";
bool force_lambert = GLOBAL_GET("rendering/shading/overrides/force_lambert_over_burley");
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index ad4bdae272..381cf1a7c6 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -1075,6 +1075,7 @@ void MeshStorage::update_mesh_instances() {
}
glEnable(GL_RASTERIZER_DISCARD);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Process skeletons and blend shapes using transform feedback
while (dirty_mesh_instance_arrays.first()) {
MeshInstance *mi = dirty_mesh_instance_arrays.first()->self();
@@ -1284,13 +1285,17 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::
multimesh->data_cache_used_dirty_regions = 0;
}
+ // If we have either color or custom data, reserve space for both to make data handling logic simpler.
+ // This way we can always treat them both as a single, compressed uvec4.
+ int color_and_custom_strides = (p_use_colors || p_use_custom_data) ? 2 : 0;
+
multimesh->instances = p_instances;
multimesh->xform_format = p_transform_format;
multimesh->uses_colors = p_use_colors;
multimesh->color_offset_cache = p_transform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12;
multimesh->uses_custom_data = p_use_custom_data;
- multimesh->custom_data_offset_cache = multimesh->color_offset_cache + (p_use_colors ? 2 : 0);
- multimesh->stride_cache = multimesh->custom_data_offset_cache + (p_use_custom_data ? 2 : 0);
+ multimesh->custom_data_offset_cache = multimesh->color_offset_cache + color_and_custom_strides;
+ multimesh->stride_cache = multimesh->custom_data_offset_cache + color_and_custom_strides;
multimesh->buffer_set = false;
multimesh->data_cache = Vector<float>();
diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp
index 19bf57df94..829574cae0 100644
--- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp
+++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp
@@ -37,27 +37,21 @@ RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() {
free_render_buffer_data();
}
-void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
- GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
-
- //internal_size.x = p_internal_size.x; // ignore for now
- //internal_size.y = p_internal_size.y;
- width = p_target_size.x;
- height = p_target_size.y;
- //scaling_3d_mode = p_scaling_3d_mode
- //fsr_sharpness = p_fsr_sharpness;
- //texture_mipmap_bias = p_texture_mipmap_bias;
- render_target = p_render_target;
- //msaa = p_msaa;
- //screen_space_aa = p_screen_space_aa;
- //use_debanding = p_use_debanding;
- view_count = p_view_count;
+void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p_config) {
+ //internal_size.x = p_config->get_internal_size().x; // ignore for now
+ //internal_size.y = p_config->get_internal_size().y;
+ width = p_config->get_target_size().x;
+ height = p_config->get_target_size().y;
+ //scaling_3d_mode = p_config->get_scaling_3d_mode()
+ //fsr_sharpness = p_config->get_fsr_sharpness();
+ //texture_mipmap_bias = p_config->get_texture_mipmap_bias();
+ render_target = p_config->get_render_target();
+ //msaa = p_config->get_msaa_3d();
+ //screen_space_aa = p_config->get_screen_space_aa();
+ //use_debanding = p_config->get_use_debanding();
+ view_count = p_config->get_view_count();
free_render_buffer_data();
-
- GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
-
- is_transparent = rt->is_transparent;
}
void RenderSceneBuffersGLES3::free_render_buffer_data() {
diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h
index d07a0812f6..aa98797ed1 100644
--- a/drivers/gles3/storage/render_scene_buffers_gles3.h
+++ b/drivers/gles3/storage/render_scene_buffers_gles3.h
@@ -58,8 +58,6 @@ public:
//bool use_debanding = false;
uint32_t view_count = 1;
- bool is_transparent = false;
-
RID render_target;
//built-in textures used for ping pong image processing and blurring
@@ -81,7 +79,7 @@ public:
private:
public:
virtual ~RenderSceneBuffersGLES3();
- virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override;
+ virtual void configure(const RenderSceneBuffersConfiguration *p_config) override;
virtual void set_fsr_sharpness(float p_fsr_sharpness) override{};
virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override{};
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 305be68706..a3f230f9e2 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1192,6 +1192,9 @@ Size2 TextureStorage::texture_size_with_proxy(RID p_texture) {
}
}
+void TextureStorage::texture_rd_initialize(RID p_texture, const RID &p_rd_texture, const RS::TextureLayeredType p_layer_type) {
+}
+
RID TextureStorage::texture_get_rd_texture(RID p_texture, bool p_srgb) const {
return RID();
}
@@ -1837,10 +1840,10 @@ void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) {
}
GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer, texture_size_bytes, "Render target backbuffer color texture");
- // Initialize all levels to opaque Magenta.
+ // Initialize all levels to clear black.
for (int j = 0; j < count; j++) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, j);
- glClearColor(1.0, 0.0, 1.0, 1.0);
+ glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
}
@@ -2568,18 +2571,18 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons
}
glDisable(GL_BLEND);
- //single texture copy for backbuffer
+ // Single texture copy for backbuffer.
glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, rt->color);
GLES3::CopyEffects::get_singleton()->copy_screen();
if (p_gen_mipmaps) {
- GLES3::CopyEffects::get_singleton()->bilinear_blur(rt->backbuffer, rt->mipmap_count, region);
+ GLES3::CopyEffects::get_singleton()->gaussian_blur(rt->backbuffer, rt->mipmap_count, region, rt->size);
glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
}
- glEnable(GL_BLEND); // 2D almost always uses blend.
+ glEnable(GL_BLEND); // 2D starts with blend enabled.
}
void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color) {
@@ -2624,8 +2627,10 @@ void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target,
return; //nothing to do
}
}
+ glDisable(GL_BLEND);
+ GLES3::CopyEffects::get_singleton()->gaussian_blur(rt->backbuffer, rt->mipmap_count, region, rt->size);
+ glEnable(GL_BLEND); // 2D starts with blend enabled.
- GLES3::CopyEffects::get_singleton()->bilinear_blur(rt->backbuffer, rt->mipmap_count, region);
glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
}
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index fefcd56570..bad2b31a31 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -252,10 +252,10 @@ struct Texture {
}
Config *config = Config::get_singleton();
state_filter = p_filter;
- GLenum pmin = GL_NEAREST; // param min
- GLenum pmag = GL_NEAREST; // param mag
- GLint max_lod = 1000;
- bool use_anisotropy = false;
+ GLenum pmin = GL_NEAREST;
+ GLenum pmag = GL_NEAREST;
+ GLint max_lod = 0;
+ GLfloat anisotropy = 1.0f;
switch (state_filter) {
case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST: {
pmin = GL_NEAREST;
@@ -268,7 +268,7 @@ struct Texture {
max_lod = 0;
} break;
case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: {
- use_anisotropy = true;
+ anisotropy = config->anisotropic_level;
};
[[fallthrough]];
case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: {
@@ -278,12 +278,14 @@ struct Texture {
max_lod = 0;
} else if (config->use_nearest_mip_filter) {
pmin = GL_NEAREST_MIPMAP_NEAREST;
+ max_lod = 1000;
} else {
pmin = GL_NEAREST_MIPMAP_LINEAR;
+ max_lod = 1000;
}
} break;
case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC: {
- use_anisotropy = true;
+ anisotropy = config->anisotropic_level;
};
[[fallthrough]];
case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: {
@@ -293,19 +295,22 @@ struct Texture {
max_lod = 0;
} else if (config->use_nearest_mip_filter) {
pmin = GL_LINEAR_MIPMAP_NEAREST;
+ max_lod = 1000;
} else {
pmin = GL_LINEAR_MIPMAP_LINEAR;
+ max_lod = 1000;
}
} break;
default: {
+ return;
} break;
}
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, pmin);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, pmag);
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, max_lod);
- if (config->support_anisotropic_filter && use_anisotropy) {
- glTexParameterf(target, _GL_TEXTURE_MAX_ANISOTROPY_EXT, config->anisotropic_level);
+ if (config->support_anisotropic_filter) {
+ glTexParameterf(target, _GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
}
}
void gl_set_repeat(RS::CanvasItemTextureRepeat p_repeat) {
@@ -313,8 +318,11 @@ struct Texture {
return;
}
state_repeat = p_repeat;
- GLenum prep = GL_CLAMP_TO_EDGE; // parameter repeat
+ GLenum prep = GL_CLAMP_TO_EDGE;
switch (state_repeat) {
+ case RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED: {
+ prep = GL_CLAMP_TO_EDGE;
+ } break;
case RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: {
prep = GL_REPEAT;
} break;
@@ -322,6 +330,7 @@ struct Texture {
prep = GL_MIRRORED_REPEAT;
} break;
default: {
+ return;
} break;
}
glTexParameteri(target, GL_TEXTURE_WRAP_T, prep);
@@ -330,8 +339,8 @@ struct Texture {
}
private:
- RS::CanvasItemTextureFilter state_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
- RS::CanvasItemTextureRepeat state_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
+ RS::CanvasItemTextureFilter state_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX;
+ RS::CanvasItemTextureRepeat state_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
};
struct RenderTarget {
@@ -536,11 +545,12 @@ public:
virtual Size2 texture_size_with_proxy(RID p_proxy) override;
+ virtual void texture_rd_initialize(RID p_texture, const RID &p_rd_texture, const RS::TextureLayeredType p_layer_type = RS::TEXTURE_LAYERED_2D_ARRAY) override;
virtual RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) const override;
virtual uint64_t texture_get_native_handle(RID p_texture, bool p_srgb = false) const override;
void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0);
- Image::Format texture_get_format(RID p_texture) const;
+ virtual Image::Format texture_get_format(RID p_texture) const override;
uint32_t texture_get_texid(RID p_texture) const;
uint32_t texture_get_width(RID p_texture) const;
uint32_t texture_get_height(RID p_texture) const;
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/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index 45f9f14dab..a80b7d449e 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -108,10 +108,7 @@ Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) {
last_error = ERR_FILE_CANT_OPEN;
return last_error;
}
- // Fix temporary file permissions (defaults to 0600 instead of 0666 & ~umask).
- mode_t mask = umask(022);
- umask(mask);
- fchmod(fd, 0666 & ~mask);
+ fchmod(fd, 0666);
path = String::utf8(cs.ptr());
f = fdopen(fd, mode_string);
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..d521f675fb 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)) {
@@ -2874,6 +2893,29 @@ bool RenderingDeviceVulkan::texture_is_valid(RID p_texture) {
return texture_owner.owns(p_texture);
}
+RD::TextureFormat RenderingDeviceVulkan::texture_get_format(RID p_texture) {
+ _THREAD_SAFE_METHOD_
+
+ Texture *tex = texture_owner.get_or_null(p_texture);
+ ERR_FAIL_COND_V(!tex, TextureFormat());
+
+ TextureFormat tf;
+
+ tf.format = tex->format;
+ tf.width = tex->width;
+ tf.height = tex->height;
+ tf.depth = tex->depth;
+ tf.array_layers = tex->layers;
+ tf.mipmaps = tex->mipmaps;
+ tf.texture_type = tex->type;
+ tf.samples = tex->samples;
+ tf.usage_bits = tex->usage_flags;
+ tf.shareable_formats = tex->allowed_shared_formats;
+ tf.is_resolve_buffer = tex->is_resolve_buffer;
+
+ return tf;
+}
+
Size2i RenderingDeviceVulkan::texture_size(RID p_texture) {
_THREAD_SAFE_METHOD_
@@ -3020,8 +3062,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 +3244,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 +3384,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)) {
@@ -4633,7 +4687,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve
"Number of uniform sets is larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ").");
// Collect reflection data into binary data.
- RenderingDeviceVulkanShaderBinaryData binary_data;
+ RenderingDeviceVulkanShaderBinaryData binary_data{};
Vector<Vector<RenderingDeviceVulkanShaderBinaryDataBinding>> uniform_info; // Set bindings.
Vector<RenderingDeviceVulkanShaderBinarySpecializationConstant> specialization_constants;
{
@@ -4804,7 +4858,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve
return ret;
}
-RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary) {
+RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder) {
const uint8_t *binptr = p_shader_binary.ptr();
uint32_t binsize = p_shader_binary.size();
@@ -5130,14 +5184,23 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_
ERR_FAIL_V_MSG(RID(), error_text);
}
-
- RID id = shader_owner.make_rid(shader);
+ RID id;
+ if (p_placeholder.is_null()) {
+ id = shader_owner.make_rid(shader);
+ } else {
+ shader_owner.initialize_rid(p_placeholder, shader);
+ id = p_placeholder;
+ }
#ifdef DEV_ENABLED
set_resource_name(id, "RID:" + itos(id.get_id()));
#endif
return id;
}
+RID RenderingDeviceVulkan::shader_create_placeholder() {
+ return shader_owner.allocate_rid();
+}
+
uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) {
_THREAD_SAFE_METHOD_
@@ -7651,10 +7714,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;
@@ -7731,6 +7798,8 @@ void RenderingDeviceVulkan::draw_list_end(BitField<BarrierMask> p_post_barrier)
/***********************/
RenderingDevice::ComputeListID RenderingDeviceVulkan::compute_list_begin(bool p_allow_draw_overlap) {
+ _THREAD_SAFE_METHOD_
+
ERR_FAIL_COND_V_MSG(!p_allow_draw_overlap && draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time.");
ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time.");
@@ -7745,6 +7814,8 @@ RenderingDevice::ComputeListID RenderingDeviceVulkan::compute_list_begin(bool p_
}
void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline) {
+ // Must be called within a compute list, the class mutex is locked during that time
+
ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
ERR_FAIL_COND(!compute_list);
@@ -7809,6 +7880,8 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l
}
void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index) {
+ // Must be called within a compute list, the class mutex is locked during that time
+
ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
ERR_FAIL_COND(!compute_list);
@@ -7983,6 +8056,8 @@ void RenderingDeviceVulkan::compute_list_set_push_constant(ComputeListID p_list,
}
void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) {
+ // Must be called within a compute list, the class mutex is locked during that time
+
ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
ERR_FAIL_COND(!compute_list);
@@ -8126,6 +8201,8 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list,
}
void RenderingDeviceVulkan::compute_list_add_barrier(ComputeListID p_list) {
+ // Must be called within a compute list, the class mutex is locked during that time
+
uint32_t barrier_flags = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
uint32_t access_flags = VK_ACCESS_SHADER_READ_BIT;
_compute_list_add_barrier(BARRIER_MASK_COMPUTE, barrier_flags, access_flags);
@@ -8199,10 +8276,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 +8308,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 +8328,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;
@@ -8606,6 +8691,8 @@ void RenderingDeviceVulkan::swap_buffers() {
}
void RenderingDeviceVulkan::submit() {
+ _THREAD_SAFE_METHOD_
+
ERR_FAIL_COND_MSG(local_device.is_null(), "Only local devices can submit and sync.");
ERR_FAIL_COND_MSG(local_device_processing, "device already submitted, call sync to wait until done.");
@@ -8617,6 +8704,8 @@ void RenderingDeviceVulkan::submit() {
}
void RenderingDeviceVulkan::sync() {
+ _THREAD_SAFE_METHOD_
+
ERR_FAIL_COND_MSG(local_device.is_null(), "Only local devices can submit and sync.");
ERR_FAIL_COND_MSG(!local_device_processing, "sync can only be called after a submit");
diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h
index 9c621c1d44..fd832312ac 100644
--- a/drivers/vulkan/rendering_device_vulkan.h
+++ b/drivers/vulkan/rendering_device_vulkan.h
@@ -1082,6 +1082,7 @@ public:
virtual bool texture_is_format_supported_for_usage(DataFormat p_format, BitField<RenderingDevice::TextureUsageBits> p_usage) const;
virtual bool texture_is_shared(RID p_texture);
virtual bool texture_is_valid(RID p_texture);
+ virtual TextureFormat texture_get_format(RID p_texture);
virtual Size2i texture_size(RID p_texture);
virtual uint64_t texture_get_native_handle(RID p_texture);
@@ -1134,7 +1135,8 @@ public:
virtual String shader_get_binary_cache_key() const;
virtual Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = "");
- virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary);
+ virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID());
+ virtual RID shader_create_placeholder();
virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader);
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index 7c52447e44..c167caeb7c 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -1195,12 +1195,15 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) {
VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties));
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props);
for (uint32_t j = 0; j < device_queue_family_count; j++) {
- VkBool32 supports;
- vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, p_surface, &supports);
- if (supports && ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) {
- present_supported = true;
- } else {
- continue;
+ if ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
+ VkBool32 supports;
+ err = vkGetPhysicalDeviceSurfaceSupportKHR(
+ physical_devices[i], j, p_surface, &supports);
+ if (err == VK_SUCCESS && supports) {
+ present_supported = true;
+ } else {
+ continue;
+ }
}
}
String name = props.deviceName;
@@ -1775,6 +1778,7 @@ Error VulkanContext::_clean_up_swap_chain(Window *window) {
fpDestroySwapchainKHR(device, window->swapchain, nullptr);
window->swapchain = VK_NULL_HANDLE;
vkDestroyRenderPass(device, window->render_pass, nullptr);
+ window->render_pass = VK_NULL_HANDLE;
if (window->swapchain_image_resources) {
for (uint32_t i = 0; i < swapchainImageCount; i++) {
vkDestroyImageView(device, window->swapchain_image_resources[i].view, nullptr);
@@ -1783,6 +1787,7 @@ Error VulkanContext::_clean_up_swap_chain(Window *window) {
free(window->swapchain_image_resources);
window->swapchain_image_resources = nullptr;
+ swapchainImageCount = 0;
}
if (separate_present_queue) {
vkDestroyCommandPool(device, window->present_cmd_pool, nullptr);
@@ -1802,6 +1807,16 @@ Error VulkanContext::_update_swap_chain(Window *window) {
err = fpGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, window->surface, &surfCapabilities);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
+ {
+ VkBool32 supports = VK_FALSE;
+ err = vkGetPhysicalDeviceSurfaceSupportKHR(
+ gpu, present_queue_family_index, window->surface, &supports);
+ ERR_FAIL_COND_V_MSG(err != VK_SUCCESS || supports == false, ERR_CANT_CREATE,
+ "Window's surface is not supported by device. Did the GPU go offline? Was the window "
+ "created on another monitor? Check previous errors & try launching with "
+ "--gpu-validation.");
+ }
+
uint32_t presentModeCount;
err = fpGetPhysicalDeviceSurfacePresentModesKHR(gpu, window->surface, &presentModeCount, nullptr);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 24a6d89118..3aa3aa567b 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);
@@ -3632,13 +3632,15 @@ void AnimationTrackEditor::commit_insert_queue() {
}
}
- if (bool(EDITOR_GET("editors/animation/confirm_insert_track")) && num_tracks > 0) {
+ // Skip the confirmation dialog if the user holds Shift while clicking the key icon.
+ if (!Input::get_singleton()->is_key_pressed(Key::SHIFT) && num_tracks > 0) {
+ String shortcut_hint = TTR("Hold Shift when clicking the key icon to skip this dialog.");
// Potentially a new key, does not exist.
if (num_tracks == 1) {
// TRANSLATORS: %s will be replaced by a phrase describing the target of track.
- insert_confirm_text->set_text(vformat(TTR("Create new track for %s and insert key?"), last_track_query));
+ insert_confirm_text->set_text(vformat(TTR("Create new track for %s and insert key?") + "\n\n" + shortcut_hint, last_track_query));
} else {
- insert_confirm_text->set_text(vformat(TTR("Create %d new tracks and insert keys?"), num_tracks));
+ insert_confirm_text->set_text(vformat(TTR("Create %d new tracks and insert keys?") + "\n\n" + shortcut_hint, num_tracks));
}
insert_confirm_bezier->set_visible(all_bezier);
diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp
index 6824bc7960..553f391a1d 100644
--- a/editor/animation_track_editor_plugins.cpp
+++ b/editor/animation_track_editor_plugins.cpp
@@ -875,8 +875,6 @@ void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int
Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream);
- float preview_len = preview->get_length();
-
int pixel_total_len = len * p_pixels_sec;
len -= end_ofs;
@@ -918,7 +916,7 @@ void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int
Vector<Vector2> points;
points.resize((to_x - from_x) * 2);
- preview_len = preview->get_length();
+ float preview_len = preview->get_length();
for (int i = from_x; i < to_x; i++) {
float ofs = (i - pixel_begin) * preview_len / pixel_total_len;
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 70fd738019..6c5c99698d 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -126,7 +126,7 @@ void FindReplaceBar::unhandled_input(const Ref<InputEvent> &p_event) {
if (k.is_valid() && k->is_action_pressed(SNAME("ui_cancel"), false, true)) {
Control *focus_owner = get_viewport()->gui_get_focus_owner();
- if (text_editor->has_focus() || (focus_owner && vbc_lineedit->is_ancestor_of(focus_owner))) {
+ if (text_editor->has_focus() || (focus_owner && is_ancestor_of(focus_owner))) {
_hide_bar();
accept_event();
}
@@ -891,7 +891,7 @@ void CodeTextEditor::_line_col_changed() {
sb.append(itos(positional_column + 1).lpad(3));
sb.append(" | ");
- sb.append(text_editor->is_indent_using_spaces() ? "Spaces" : "Tabs");
+ sb.append(text_editor->is_indent_using_spaces() ? TTR("Spaces", "Indentation") : TTR("Tabs", "Indentation"));
line_and_col_txt->set_text(sb.as_string());
@@ -905,7 +905,7 @@ void CodeTextEditor::_line_col_changed() {
}
void CodeTextEditor::_text_changed() {
- if (text_editor->is_insert_text_operation()) {
+ if (code_complete_enabled && text_editor->is_insert_text_operation()) {
code_complete_timer_line = text_editor->get_caret_line();
code_complete_timer->start();
}
@@ -1485,12 +1485,13 @@ void CodeTextEditor::toggle_inline_comment(const String &delimiter) {
// Check first if there's any uncommented lines in selection.
bool is_commented = true;
for (int line = from; line <= to; line++) {
- if (!text_editor->get_line(line).begins_with(delimiter)) {
+ // `+ delimiter.length()` here because comment delimiter is not actually `in comment` so we check first character after it
+ int delimiter_idx = text_editor->is_in_comment(line, text_editor->get_first_non_whitespace_column(line) + delimiter.length());
+ if (delimiter_idx == -1 || text_editor->get_delimiter_start_key(delimiter_idx) != delimiter) {
is_commented = false;
break;
}
}
-
// Caret positions need to be saved since they could be moved at the eol.
Vector<int> caret_cols;
Vector<int> selection_to_cols;
@@ -1511,10 +1512,10 @@ void CodeTextEditor::toggle_inline_comment(const String &delimiter) {
continue;
}
if (is_commented) {
- text_editor->set_line(line, line_text.substr(delimiter.length(), line_text.length()));
- continue;
+ text_editor->set_line(line, line_text.replace_first(delimiter, ""));
+ } else {
+ text_editor->set_line(line, line_text.insert(text_editor->get_first_non_whitespace_column(line), delimiter));
}
- text_editor->set_line(line, delimiter + line_text);
}
// Readjust carets and selections.
@@ -1736,6 +1737,7 @@ void CodeTextEditor::_apply_settings_change() {
text_editor->set_code_hint_draw_below(EDITOR_GET("text_editor/completion/put_callhint_tooltip_below_current_line"));
+ code_complete_enabled = EDITOR_GET("text_editor/completion/code_complete_enabled");
code_complete_timer->set_wait_time(EDITOR_GET("text_editor/completion/code_complete_delay"));
idle->set_wait_time(EDITOR_GET("text_editor/completion/idle_parse_delay"));
}
@@ -2010,6 +2012,7 @@ CodeTextEditor::CodeTextEditor() {
idle->set_one_shot(true);
idle->set_wait_time(EDITOR_GET("text_editor/completion/idle_parse_delay"));
+ code_complete_enabled = EDITOR_GET("text_editor/completion/code_complete_enabled");
code_complete_timer = memnew(Timer);
add_child(code_complete_timer);
code_complete_timer->set_one_shot(true);
diff --git a/editor/code_editor.h b/editor/code_editor.h
index a83bb96771..c18154a1ef 100644
--- a/editor/code_editor.h
+++ b/editor/code_editor.h
@@ -157,6 +157,7 @@ class CodeTextEditor : public VBoxContainer {
Label *info = nullptr;
Timer *idle = nullptr;
+ bool code_complete_enabled = true;
Timer *code_complete_timer = nullptr;
int code_complete_timer_line = 0;
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_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp
index e083e1746d..1e9b7c2c60 100644
--- a/editor/debugger/editor_debugger_inspector.cpp
+++ b/editor/debugger/editor_debugger_inspector.cpp
@@ -232,7 +232,7 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
PropertyHint h = PROPERTY_HINT_NONE;
String hs;
- if (v.get_type() == Variant::OBJECT) {
+ if (var.var_type == Variant::OBJECT) {
v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id();
h = PROPERTY_HINT_OBJECT_ID;
hs = "Object";
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/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index 8985387043..2c40f0e120 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -71,10 +71,12 @@
using CameraOverride = EditorDebuggerNode::CameraOverride;
-void ScriptEditorDebugger::_put_msg(String p_message, Array p_data) {
+void ScriptEditorDebugger::_put_msg(String p_message, Array p_data, uint64_t p_thread_id) {
+ ERR_FAIL_COND(p_thread_id == Thread::UNASSIGNED_ID);
if (is_session_active()) {
Array msg;
msg.push_back(p_message);
+ msg.push_back(p_thread_id);
msg.push_back(p_data);
peer->put_message(msg);
}
@@ -98,31 +100,31 @@ void ScriptEditorDebugger::debug_skip_breakpoints() {
Array msg;
msg.push_back(skip_breakpoints_value);
- _put_msg("set_skip_breakpoints", msg);
+ _put_msg("set_skip_breakpoints", msg, debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
}
void ScriptEditorDebugger::debug_next() {
- ERR_FAIL_COND(!breaked);
+ ERR_FAIL_COND(!is_breaked());
- _put_msg("next", Array());
+ _put_msg("next", Array(), debugging_thread_id);
_clear_execution();
}
void ScriptEditorDebugger::debug_step() {
- ERR_FAIL_COND(!breaked);
+ ERR_FAIL_COND(!is_breaked());
- _put_msg("step", Array());
+ _put_msg("step", Array(), debugging_thread_id);
_clear_execution();
}
void ScriptEditorDebugger::debug_break() {
- ERR_FAIL_COND(breaked);
+ ERR_FAIL_COND(is_breaked());
_put_msg("break", Array());
}
void ScriptEditorDebugger::debug_continue() {
- ERR_FAIL_COND(!breaked);
+ ERR_FAIL_COND(!is_breaked());
// Allow focus stealing only if we actually run this client for security.
if (remote_pid && EditorNode::get_singleton()->has_child_process(remote_pid)) {
@@ -130,7 +132,7 @@ void ScriptEditorDebugger::debug_continue() {
}
_clear_execution();
- _put_msg("continue", Array());
+ _put_msg("continue", Array(), debugging_thread_id);
_put_msg("servers:foreground", Array());
}
@@ -299,43 +301,89 @@ Size2 ScriptEditorDebugger::get_minimum_size() const {
return ms;
}
-void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_data) {
+void ScriptEditorDebugger::_thread_debug_enter(uint64_t p_thread_id) {
+ ERR_FAIL_COND(!threads_debugged.has(p_thread_id));
+ ThreadDebugged &td = threads_debugged[p_thread_id];
+ _set_reason_text(td.error, MESSAGE_ERROR);
+ emit_signal(SNAME("breaked"), true, td.can_debug, td.error, td.has_stackdump);
+ if (!td.error.is_empty()) {
+ tabs->set_current_tab(0);
+ }
+ inspector->clear_cache(); // Take a chance to force remote objects update.
+ _put_msg("get_stack_dump", Array(), p_thread_id);
+}
+
+void ScriptEditorDebugger::_select_thread(int p_index) {
+ debugging_thread_id = threads->get_item_metadata(threads->get_selected());
+ _thread_debug_enter(debugging_thread_id);
+}
+
+void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread_id, const Array &p_data) {
emit_signal(SNAME("debug_data"), p_msg, p_data);
if (p_msg == "debug_enter") {
- _put_msg("get_stack_dump", Array());
-
- ERR_FAIL_COND(p_data.size() != 3);
- bool can_continue = p_data[0];
- String error = p_data[1];
- bool has_stackdump = p_data[2];
- breaked = true;
- can_request_idle_draw = true;
- can_debug = can_continue;
- _update_buttons_state();
- _set_reason_text(error, MESSAGE_ERROR);
- emit_signal(SNAME("breaked"), true, can_continue, error, has_stackdump);
- if (is_move_to_foreground()) {
- DisplayServer::get_singleton()->window_move_to_foreground();
- }
- if (!error.is_empty()) {
- tabs->set_current_tab(0);
+ ERR_FAIL_COND(p_data.size() != 4);
+
+ ThreadDebugged td;
+ td.name = p_data[3];
+ td.error = p_data[1];
+ td.can_debug = p_data[0];
+ td.has_stackdump = p_data[2];
+ td.thread_id = p_thread_id;
+ static uint32_t order_inc = 0;
+ td.debug_order = order_inc++;
+
+ threads_debugged.insert(p_thread_id, td);
+
+ if (threads_debugged.size() == 1) {
+ // First thread that requests debug
+ debugging_thread_id = p_thread_id;
+ _thread_debug_enter(p_thread_id);
+ can_request_idle_draw = true;
+ if (is_move_to_foreground()) {
+ DisplayServer::get_singleton()->window_move_to_foreground();
+ }
+ profiler->set_enabled(false, false);
+ visual_profiler->set_enabled(false);
}
- profiler->set_enabled(false, false);
- visual_profiler->set_enabled(false);
- inspector->clear_cache(); // Take a chance to force remote objects update.
+ _update_buttons_state();
} else if (p_msg == "debug_exit") {
- breaked = false;
- can_debug = false;
- _clear_execution();
- _update_buttons_state();
- _set_reason_text(TTR("Execution resumed."), MESSAGE_SUCCESS);
- emit_signal(SNAME("breaked"), false, false, "", false);
+ threads_debugged.erase(p_thread_id);
+ if (p_thread_id == debugging_thread_id) {
+ _clear_execution();
+ if (threads_debugged.size() == 0) {
+ debugging_thread_id = Thread::UNASSIGNED_ID;
+ } else {
+ // Find next thread to debug.
+ uint32_t min_order = 0xFFFFFFFF;
+ uint64_t next_thread = Thread::UNASSIGNED_ID;
+ for (KeyValue<uint64_t, ThreadDebugged> T : threads_debugged) {
+ if (T.value.debug_order < min_order) {
+ min_order = T.value.debug_order;
+ next_thread = T.key;
+ }
+ }
+
+ debugging_thread_id = next_thread;
+ }
- profiler->set_enabled(true, false);
- profiler->disable_seeking();
+ if (debugging_thread_id == Thread::UNASSIGNED_ID) {
+ // Nothing else to debug.
+ profiler->set_enabled(true, false);
+ profiler->disable_seeking();
- visual_profiler->set_enabled(true);
+ visual_profiler->set_enabled(true);
+
+ _set_reason_text(TTR("Execution resumed."), MESSAGE_SUCCESS);
+ emit_signal(SNAME("breaked"), false, false, "", false);
+
+ _update_buttons_state();
+ } else {
+ _thread_debug_enter(debugging_thread_id);
+ }
+ } else {
+ _update_buttons_state();
+ }
} else if (p_msg == "set_pid") {
ERR_FAIL_COND(p_data.size() < 1);
@@ -379,7 +427,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
vmem_total->set_tooltip_text(TTR("Bytes:") + " " + itos(total));
vmem_total->set_text(String::humanize_size(total));
-
} else if (p_msg == "servers:drawn") {
can_request_idle_draw = true;
} else if (p_msg == "stack_dump") {
@@ -414,11 +461,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
inspector->clear_stack_variables();
ERR_FAIL_COND(p_data.size() != 1);
emit_signal(SNAME("stack_frame_vars"), p_data[0]);
-
} else if (p_msg == "stack_frame_var") {
inspector->add_stack_variable(p_data);
emit_signal(SNAME("stack_frame_var"), p_data);
-
} else if (p_msg == "output") {
ERR_FAIL_COND(p_data.size() != 2);
@@ -458,7 +503,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
frame_data.write[i] = p_data[i];
}
performance_profiler->add_profile_frame(frame_data);
-
} else if (p_msg == "visual:profile_frame") {
ServersDebugger::VisualProfilerFrame frame;
frame.deserialize(p_data);
@@ -477,7 +521,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
}
}
visual_profiler->add_frame_metric(metric);
-
} else if (p_msg == "error") {
DebuggerMarshalls::OutputError oe;
ERR_FAIL_COND_MSG(oe.deserialize(p_data) == false, "Failed to deserialize error message");
@@ -625,13 +668,11 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
} else {
error_count++;
}
-
} else if (p_msg == "servers:function_signature") {
// Cache a profiler signature.
ServersDebugger::ScriptFunctionSignature sig;
sig.deserialize(p_data);
profiler_signature[sig.id] = sig.name;
-
} else if (p_msg == "servers:profile_frame" || p_msg == "servers:profile_total") {
EditorProfiler::Metric metric;
ServersDebugger::ServersProfilerFrame frame;
@@ -744,11 +785,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
} else {
profiler->add_frame_metric(metric, true);
}
-
} else if (p_msg == "request_quit") {
emit_signal(SNAME("stop_requested"));
_stop_and_notify();
-
} else if (p_msg == "performance:profile_names") {
Vector<StringName> monitors;
monitors.resize(p_data.size());
@@ -757,13 +796,11 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
monitors.set(i, p_data[i]);
}
performance_profiler->update_monitors(monitors);
-
} else if (p_msg == "filesystem:update_file") {
ERR_FAIL_COND(p_data.size() < 1);
if (EditorFileSystem::get_singleton()) {
EditorFileSystem::get_singleton()->update_file(p_data[0]);
}
-
} else {
int colon_index = p_msg.find_char(':');
ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");
@@ -878,7 +915,7 @@ void ScriptEditorDebugger::_notification(int p_what) {
msg.push_back(cam->get_far());
_put_msg("scene:override_camera_3D:transform", msg);
}
- if (breaked && can_request_idle_draw) {
+ if (is_breaked() && can_request_idle_draw) {
_put_msg("servers:draw", Array());
can_request_idle_draw = false;
}
@@ -888,11 +925,12 @@ void ScriptEditorDebugger::_notification(int p_what) {
while (peer.is_valid() && peer->has_message()) {
Array arr = peer->get_message();
- if (arr.size() != 2 || arr[0].get_type() != Variant::STRING || arr[1].get_type() != Variant::ARRAY) {
+ if (arr.size() != 3 || arr[0].get_type() != Variant::STRING || arr[1].get_type() != Variant::INT || arr[2].get_type() != Variant::ARRAY) {
_stop_and_notify();
ERR_FAIL_MSG("Invalid message format received from peer");
}
- _parse_message(arr[0], arr[1]);
+
+ _parse_message(arr[0], arr[1], arr[2]);
if (OS::get_singleton()->get_ticks_msec() > until) {
break;
@@ -959,8 +997,6 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {
performance_profiler->reset();
set_process(true);
- breaked = false;
- can_debug = true;
camera_override = CameraOverride::OVERRIDE_NONE;
tabs->set_current_tab(0);
@@ -973,13 +1009,35 @@ void ScriptEditorDebugger::_update_buttons_state() {
const bool active = is_session_active();
const bool has_editor_tree = active && editor_remote_tree && editor_remote_tree->get_selected();
vmem_refresh->set_disabled(!active);
- step->set_disabled(!active || !breaked || !can_debug);
- next->set_disabled(!active || !breaked || !can_debug);
- copy->set_disabled(!active || !breaked);
- docontinue->set_disabled(!active || !breaked);
- dobreak->set_disabled(!active || breaked);
+ step->set_disabled(!active || !is_breaked() || !is_debuggable());
+ next->set_disabled(!active || !is_breaked() || !is_debuggable());
+ copy->set_disabled(!active || !is_breaked());
+ docontinue->set_disabled(!active || !is_breaked());
+ dobreak->set_disabled(!active || is_breaked());
le_clear->set_disabled(!active);
le_set->set_disabled(!has_editor_tree);
+
+ thread_list_updating = true;
+ LocalVector<ThreadDebugged *> threadss;
+ for (KeyValue<uint64_t, ThreadDebugged> &I : threads_debugged) {
+ threadss.push_back(&I.value);
+ }
+
+ threadss.sort_custom<ThreadSort>();
+ threads->clear();
+ int32_t selected_index = -1;
+ for (uint32_t i = 0; i < threadss.size(); i++) {
+ if (debugging_thread_id == threadss[i]->thread_id) {
+ selected_index = i;
+ }
+ threads->add_item(threadss[i]->name);
+ threads->set_item_metadata(threads->get_item_count() - 1, threadss[i]->thread_id);
+ }
+ if (selected_index != -1) {
+ threads->select(selected_index);
+ }
+
+ thread_list_updating = false;
}
void ScriptEditorDebugger::_stop_and_notify() {
@@ -990,8 +1048,8 @@ void ScriptEditorDebugger::_stop_and_notify() {
void ScriptEditorDebugger::stop() {
set_process(false);
- breaked = false;
- can_debug = false;
+ threads_debugged.clear();
+ debugging_thread_id = Thread::UNASSIGNED_ID;
remote_pid = 0;
_clear_execution();
@@ -1043,7 +1101,7 @@ void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) {
}
void ScriptEditorDebugger::_profiler_seeked() {
- if (breaked) {
+ if (is_breaked()) {
return;
}
debug_break();
@@ -1067,7 +1125,7 @@ void ScriptEditorDebugger::_export_csv() {
}
String ScriptEditorDebugger::get_var_value(const String &p_var) const {
- if (!breaked) {
+ if (!is_breaked()) {
return String();
}
return inspector->get_stack_variable(p_var);
@@ -1255,7 +1313,7 @@ bool ScriptEditorDebugger::request_stack_dump(const int &p_frame) {
Array msg;
msg.push_back(p_frame);
- _put_msg("get_stack_frame_vars", msg);
+ _put_msg("get_stack_frame_vars", msg, debugging_thread_id);
return true;
}
@@ -1407,7 +1465,7 @@ void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool
msg.push_back(p_path);
msg.push_back(p_line);
msg.push_back(p_enabled);
- _put_msg("breakpoint", msg);
+ _put_msg("breakpoint", msg, debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
TreeItem *path_item = breakpoints_tree->search_item_text(p_path);
if (path_item == nullptr) {
@@ -1450,7 +1508,7 @@ void ScriptEditorDebugger::set_breakpoint(const String &p_path, int p_line, bool
}
void ScriptEditorDebugger::reload_scripts() {
- _put_msg("reload_scripts", Array());
+ _put_msg("reload_scripts", Array(), debugging_thread_id != Thread::UNASSIGNED_ID ? debugging_thread_id : Thread::MAIN_ID);
}
bool ScriptEditorDebugger::is_skip_breakpoints() {
@@ -1804,15 +1862,26 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
sc->set_h_size_flags(SIZE_EXPAND_FILL);
parent_sc->add_child(sc);
+ VBoxContainer *stack_vb = memnew(VBoxContainer);
+ stack_vb->set_h_size_flags(SIZE_EXPAND_FILL);
+ sc->add_child(stack_vb);
+ HBoxContainer *thread_hb = memnew(HBoxContainer);
+ stack_vb->add_child(thread_hb);
+ thread_hb->add_child(memnew(Label(TTR("Thread:"))));
+ threads = memnew(OptionButton);
+ thread_hb->add_child(threads);
+ threads->set_h_size_flags(SIZE_EXPAND_FILL);
+ threads->connect("item_selected", callable_mp(this, &ScriptEditorDebugger::_select_thread));
+
stack_dump = memnew(Tree);
stack_dump->set_allow_reselect(true);
stack_dump->set_columns(1);
stack_dump->set_column_titles_visible(true);
stack_dump->set_column_title(0, TTR("Stack Frames"));
- stack_dump->set_h_size_flags(SIZE_EXPAND_FILL);
stack_dump->set_hide_root(true);
+ stack_dump->set_v_size_flags(SIZE_EXPAND_FILL);
stack_dump->connect("cell_selected", callable_mp(this, &ScriptEditorDebugger::_stack_dump_frame_selected));
- sc->add_child(stack_dump);
+ stack_vb->add_child(stack_dump);
VBoxContainer *inspector_vbox = memnew(VBoxContainer);
inspector_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h
index 336a113163..7e9a767273 100644
--- a/editor/debugger/script_editor_debugger.h
+++ b/editor/debugger/script_editor_debugger.h
@@ -138,6 +138,7 @@ private:
Tree *stack_dump = nullptr;
LineEdit *search = nullptr;
+ OptionButton *threads = nullptr;
EditorDebuggerInspector *inspector = nullptr;
SceneDebuggerTree *scene_tree = nullptr;
@@ -152,19 +153,39 @@ private:
EditorPerformanceProfiler *performance_profiler = nullptr;
OS::ProcessID remote_pid = 0;
- bool breaked = false;
- bool can_debug = false;
bool move_to_foreground = true;
bool can_request_idle_draw = false;
bool live_debug;
+ uint64_t debugging_thread_id = Thread::UNASSIGNED_ID;
+
+ struct ThreadDebugged {
+ String name;
+ String error;
+ bool can_debug = false;
+ bool has_stackdump = false;
+ uint32_t debug_order = 0;
+ uint64_t thread_id = Thread::UNASSIGNED_ID; // for order
+ };
+
+ struct ThreadSort {
+ bool operator()(const ThreadDebugged *a, const ThreadDebugged *b) const {
+ return a->debug_order < b->debug_order;
+ }
+ };
+
+ HashMap<uint64_t, ThreadDebugged> threads_debugged;
+ bool thread_list_updating = false;
+
+ void _select_thread(int p_index);
+
EditorDebuggerNode::CameraOverride camera_override;
void _stack_dump_frame_selected();
void _file_selected(const String &p_file);
- void _parse_message(const String &p_msg, const Array &p_data);
+ void _parse_message(const String &p_msg, uint64_t p_thread_id, const Array &p_data);
void _set_reason_text(const String &p_reason, MessageType p_type);
void _update_buttons_state();
void _remote_object_selected(ObjectID p_object);
@@ -200,7 +221,7 @@ private:
void _item_menu_id_pressed(int p_option);
void _tab_changed(int p_tab);
- void _put_msg(String p_message, Array p_data);
+ void _put_msg(String p_message, Array p_data, uint64_t p_thread_id = Thread::MAIN_ID);
void _export_csv();
void _clear_execution();
@@ -213,6 +234,8 @@ private:
String _format_frame_text(const ScriptLanguage::StackInfo *info);
+ void _thread_debug_enter(uint64_t p_thread_id);
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -238,9 +261,9 @@ public:
void debug_step();
void debug_break();
void debug_continue();
- bool is_breaked() const { return breaked; }
- bool is_debuggable() const { return can_debug; }
- bool is_session_active() { return peer.is_valid() && peer->is_peer_connected(); };
+ bool is_breaked() const { return threads_debugged.size() > 0; }
+ bool is_debuggable() const { return threads_debugged.size() > 0 && threads_debugged[debugging_thread_id].can_debug; }
+ bool is_session_active() { return peer.is_valid() && peer->is_peer_connected(); }
int get_remote_pid() const { return remote_pid; }
bool is_move_to_foreground() const;
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index 981e276b69..7929c4b0ca 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -36,6 +36,7 @@
#include "core/io/compression.h"
#include "core/io/dir_access.h"
#include "core/io/marshalls.h"
+#include "core/io/resource_importer.h"
#include "core/object/script_language.h"
#include "core/string/translation.h"
#include "editor/editor_settings.h"
@@ -386,7 +387,13 @@ void DocTools::generate(bool p_basic_types) {
List<PropertyInfo> properties;
List<PropertyInfo> own_properties;
- // Special case for editor and project settings, so they can be documented.
+ // Special cases for editor/project settings, and ResourceImporter classes,
+ // we have to rely on Object's property list to get settings and import options.
+ // Otherwise we just use ClassDB's property list (pure registered properties).
+
+ bool properties_from_instance = true; // To skip `script`, etc.
+ bool import_option = false; // Special case for default value.
+ HashMap<StringName, Variant> import_options_default;
if (name == "EditorSettings") {
// We don't create the full blown EditorSettings (+ config file) with `create()`,
// instead we just make a local instance to get default values.
@@ -396,7 +403,20 @@ void DocTools::generate(bool p_basic_types) {
} else if (name == "ProjectSettings") {
ProjectSettings::get_singleton()->get_property_list(&properties);
own_properties = properties;
+ } else if (ClassDB::is_parent_class(name, "ResourceImporter") && name != "EditorImportPlugin" && ClassDB::can_instantiate(name)) {
+ import_option = true;
+ ResourceImporter *resimp = Object::cast_to<ResourceImporter>(ClassDB::instantiate(name));
+ List<ResourceImporter::ImportOption> options;
+ resimp->get_import_options("", &options);
+ for (int i = 0; i < options.size(); i++) {
+ const PropertyInfo &prop = options[i].option;
+ properties.push_back(prop);
+ import_options_default[prop.name] = options[i].default_value;
+ }
+ own_properties = properties;
+ memdelete(resimp);
} else if (name.begins_with("EditorExportPlatform") && ClassDB::can_instantiate(name)) {
+ properties_from_instance = false;
Ref<EditorExportPlatform> platform = Object::cast_to<EditorExportPlatform>(ClassDB::instantiate(name));
if (platform.is_valid()) {
List<EditorExportPlatform::ExportOption> options;
@@ -407,6 +427,7 @@ void DocTools::generate(bool p_basic_types) {
own_properties = properties;
}
} else {
+ properties_from_instance = false;
ClassDB::get_property_list(name, &properties);
ClassDB::get_property_list(name, &own_properties, true);
}
@@ -423,6 +444,13 @@ void DocTools::generate(bool p_basic_types) {
EO = EO->next();
}
+ if (properties_from_instance) {
+ if (E.name == "resource_local_to_scene" || E.name == "resource_name" || E.name == "resource_path" || E.name == "script") {
+ // Don't include spurious properties from Object property list.
+ continue;
+ }
+ }
+
if (E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP || E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_INTERNAL || (E.type == Variant::NIL && E.usage & PROPERTY_USAGE_ARRAY)) {
continue;
}
@@ -442,22 +470,9 @@ void DocTools::generate(bool p_basic_types) {
bool default_value_valid = false;
Variant default_value;
- if (name == "EditorSettings") {
- if (E.name == "resource_local_to_scene" || E.name == "resource_name" || E.name == "resource_path" || E.name == "script") {
- // Don't include spurious properties in the generated EditorSettings class reference.
- continue;
- }
- }
-
- if (name.begins_with("EditorExportPlatform")) {
- if (E.name == "script") {
- continue;
- }
- }
-
if (name == "ProjectSettings") {
// Special case for project settings, so that settings are not taken from the current project's settings
- if (E.name == "script" || !ProjectSettings::get_singleton()->is_builtin_setting(E.name)) {
+ if (!ProjectSettings::get_singleton()->is_builtin_setting(E.name)) {
continue;
}
if (E.usage & PROPERTY_USAGE_EDITOR) {
@@ -466,6 +481,9 @@ void DocTools::generate(bool p_basic_types) {
default_value_valid = true;
}
}
+ } else if (import_option) {
+ default_value = import_options_default[E.name];
+ default_value_valid = true;
} else {
default_value = get_documentation_default_value(name, E.name, default_value_valid);
if (inherited) {
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index af7163eec1..0b2c2bea15 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -1154,15 +1154,6 @@ Ref<Texture2D> EditorData::get_script_icon(const Ref<Script> &p_script) {
return ext_icon;
}
- // Look for the base type in the editor theme.
- // This is only relevant for built-in classes.
- const Control *gui_base = EditorNode::get_singleton()->get_gui_base();
- if (gui_base && gui_base->has_theme_icon(base_type, SNAME("EditorIcons"))) {
- Ref<Texture2D> theme_icon = gui_base->get_theme_icon(base_type, SNAME("EditorIcons"));
- _script_icon_cache[p_script] = theme_icon;
- return theme_icon;
- }
-
// If no icon found, cache it as null.
_script_icon_cache[p_script] = Ref<Texture>();
return nullptr;
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index 7c77fec81a..308bf33da5 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -419,13 +419,7 @@ void EditorFeatureProfileManager::_update_profile_list(const String &p_select_pr
void EditorFeatureProfileManager::_profile_action(int p_action) {
switch (p_action) {
case PROFILE_CLEAR: {
- EditorSettings::get_singleton()->set("_default_feature_profile", "");
- EditorSettings::get_singleton()->save();
- current_profile = "";
- current.unref();
-
- _update_profile_list();
- _emit_current_profile_changed();
+ set_current_profile("", false);
} break;
case PROFILE_SET: {
String selected = _get_selected_profile();
@@ -433,13 +427,7 @@ void EditorFeatureProfileManager::_profile_action(int p_action) {
if (selected == current_profile) {
return; // Nothing to do here.
}
- EditorSettings::get_singleton()->set("_default_feature_profile", selected);
- EditorSettings::get_singleton()->save();
- current_profile = selected;
- current = edited;
-
- _update_profile_list();
- _emit_current_profile_changed();
+ set_current_profile(selected, false);
} break;
case PROFILE_IMPORT: {
import_profiles->popup_file_dialog();
@@ -878,11 +866,45 @@ Ref<EditorFeatureProfile> EditorFeatureProfileManager::get_current_profile() {
return current;
}
+String EditorFeatureProfileManager::get_current_profile_name() const {
+ return current_profile;
+}
+
+void EditorFeatureProfileManager::set_current_profile(const String &p_profile_name, bool p_validate_profile) {
+ if (p_validate_profile && !p_profile_name.is_empty()) {
+ // Profile may not exist.
+ Ref<DirAccess> da = DirAccess::open(EditorPaths::get_singleton()->get_feature_profiles_dir());
+ ERR_FAIL_COND_MSG(da.is_null(), "Cannot open directory '" + EditorPaths::get_singleton()->get_feature_profiles_dir() + "'.");
+ ERR_FAIL_COND_MSG(!da->file_exists(p_profile_name + ".profile"), "Feature profile '" + p_profile_name + "' does not exist.");
+
+ // Change profile selection to emulate the UI interaction. Otherwise, the wrong profile would get activated.
+ // FIXME: Ideally, _update_selected_profile() should not rely on the user interface state to function properly.
+ for (int i = 0; i < profile_list->get_item_count(); i++) {
+ if (profile_list->get_item_metadata(i) == p_profile_name) {
+ profile_list->select(i);
+ break;
+ }
+ }
+ _update_selected_profile();
+ }
+
+ // Store in editor settings.
+ EditorSettings::get_singleton()->set("_default_feature_profile", p_profile_name);
+ EditorSettings::get_singleton()->save();
+
+ current_profile = p_profile_name;
+ if (p_profile_name.is_empty()) {
+ current.unref();
+ } else {
+ current = edited;
+ }
+ _update_profile_list();
+ _emit_current_profile_changed();
+}
+
EditorFeatureProfileManager *EditorFeatureProfileManager::singleton = nullptr;
void EditorFeatureProfileManager::_bind_methods() {
- ClassDB::bind_method("_update_selected_profile", &EditorFeatureProfileManager::_update_selected_profile);
-
ADD_SIGNAL(MethodInfo("current_feature_profile_changed"));
}
diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h
index 3f70e03ca8..25ee1c9ba4 100644
--- a/editor/editor_feature_profile.h
+++ b/editor/editor_feature_profile.h
@@ -177,6 +177,8 @@ protected:
public:
Ref<EditorFeatureProfile> get_current_profile();
+ String get_current_profile_name() const;
+ void set_current_profile(const String &p_profile_name, bool p_validate_profile);
void notify_changed();
static EditorFeatureProfileManager *get_singleton() { return singleton; }
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 914e146d13..6f3b6ecdb0 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -544,7 +544,7 @@ bool EditorFileSystem::_scan_import_support(Vector<String> reimports) {
}
for (int i = 0; i < reimports.size(); i++) {
- HashMap<String, int>::Iterator E = import_support_test.find(reimports[i].get_extension());
+ HashMap<String, int>::Iterator E = import_support_test.find(reimports[i].get_extension().to_lower());
if (E) {
import_support_tested.write[E->value] = true;
}
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 5a3841e4ca..7573fcd21e 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -45,37 +45,43 @@
#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;
-class DocCache : public Resource {
- GDCLASS(DocCache, Resource);
- RES_BASE_EXTENSION("doc_cache");
-
- String version_hash;
- Array classes;
-
-protected:
- static void _bind_methods() {
- ClassDB::bind_method(D_METHOD("set_version_hash", "version_hash"), &DocCache::set_version_hash);
- ClassDB::bind_method(D_METHOD("get_version_hash"), &DocCache::get_version_hash);
-
- ClassDB::bind_method(D_METHOD("set_classes", "classes"), &DocCache::set_classes);
- ClassDB::bind_method(D_METHOD("get_classes"), &DocCache::get_classes);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "version_hash"), "set_version_hash", "get_version_hash");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "classes"), "set_classes", "get_classes");
- }
-
-public:
- String get_version_hash() const { return version_hash; }
- void set_version_hash(const String &p_version_hash) { version_hash = p_version_hash; }
-
- Array get_classes() const { return classes; }
- void set_classes(const Array &p_classes) { classes = p_classes; }
-};
-
static bool _attempt_doc_load(const String &p_class) {
// Docgen always happens in the outer-most class: it also generates docs for inner classes.
String outer_class = p_class.get_slice(".", 0);
@@ -308,7 +314,7 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum, bool p_is
bool can_ref = !p_type.contains("*") || is_enum_type;
String link_t = p_type; // For links in metadata
- String display_t = link_t; // For display purposes
+ String display_t; // For display purposes.
if (is_enum_type) {
link_t = p_enum; // The link for enums is always the full enum description
display_t = _contextualize_class_specifier(p_enum, edited_class);
@@ -888,10 +894,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()) {
@@ -1037,6 +1059,7 @@ void EditorHelp::_update_doc() {
if (cd.properties[i].is_deprecated) {
DEPRECATED_DOC_TAG;
}
+
if (cd.properties[i].is_experimental) {
EXPERIMENTAL_DOC_TAG;
}
@@ -1281,6 +1304,7 @@ void EditorHelp::_update_doc() {
if (cd.signals[i].is_deprecated) {
DEPRECATED_DOC_TAG;
}
+
if (cd.signals[i].is_experimental) {
EXPERIMENTAL_DOC_TAG;
}
@@ -1341,6 +1365,7 @@ void EditorHelp::_update_doc() {
enum_line[E.key] = class_desc->get_paragraph_count() - 2;
_push_code_font();
+
class_desc->push_color(theme_cache.title_color);
if (E.value.size() && E.value[0].is_bitfield) {
class_desc->add_text("flags ");
@@ -1357,21 +1382,32 @@ void EditorHelp::_update_doc() {
class_desc->push_color(theme_cache.headline_color);
class_desc->add_text(e);
class_desc->pop();
- _pop_code_font();
class_desc->push_color(theme_cache.symbol_color);
class_desc->add_text(":");
class_desc->pop();
+ if (cd.enums.has(e)) {
+ if (cd.enums[e].is_deprecated) {
+ DEPRECATED_DOC_TAG;
+ }
+
+ if (cd.enums[e].is_experimental) {
+ EXPERIMENTAL_DOC_TAG;
+ }
+ }
+
+ _pop_code_font();
+
class_desc->add_newline();
class_desc->add_newline();
// Enum description.
- if (e != "@unnamed_enums" && cd.enums.has(e) && !cd.enums[e].strip_edges().is_empty()) {
+ if (e != "@unnamed_enums" && cd.enums.has(e) && !cd.enums[e].description.strip_edges().is_empty()) {
class_desc->push_color(theme_cache.text_color);
_push_normal_font();
class_desc->push_indent(1);
- _add_text(cd.enums[e]);
+ _add_text(cd.enums[e].description);
class_desc->pop();
_pop_normal_font();
class_desc->pop();
@@ -1395,6 +1431,7 @@ void EditorHelp::_update_doc() {
constant_line[enum_list[i].name] = class_desc->get_paragraph_count() - 2;
_push_code_font();
+
_add_bulletpoint();
class_desc->push_color(theme_cache.headline_color);
_add_text(enum_list[i].name);
@@ -1405,7 +1442,6 @@ void EditorHelp::_update_doc() {
class_desc->push_color(theme_cache.value_color);
_add_text(_fix_constant(enum_list[i].value));
class_desc->pop();
- _pop_code_font();
if (enum_list[i].is_deprecated) {
DEPRECATED_DOC_TAG;
@@ -1415,6 +1451,8 @@ void EditorHelp::_update_doc() {
EXPERIMENTAL_DOC_TAG;
}
+ _pop_code_font();
+
class_desc->add_newline();
if (!enum_list[i].description.strip_edges().is_empty()) {
@@ -1481,8 +1519,6 @@ void EditorHelp::_update_doc() {
_add_text(_fix_constant(constants[i].value));
class_desc->pop();
- _pop_code_font();
-
if (constants[i].is_deprecated) {
DEPRECATED_DOC_TAG;
}
@@ -1491,6 +1527,8 @@ void EditorHelp::_update_doc() {
EXPERIMENTAL_DOC_TAG;
}
+ _pop_code_font();
+
class_desc->add_newline();
if (!constants[i].description.strip_edges().is_empty()) {
@@ -1670,6 +1708,7 @@ void EditorHelp::_update_doc() {
if (cd.properties[i].is_deprecated) {
DEPRECATED_DOC_TAG;
}
+
if (cd.properties[i].is_experimental) {
EXPERIMENTAL_DOC_TAG;
}
@@ -1906,7 +1945,7 @@ void EditorHelp::_help_callback(const String &p_topic) {
}
}
-static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, Control *p_owner_node) {
+static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, Control *p_owner_node, const String &p_class = "") {
DocTools *doc = EditorHelp::get_doc_data();
String base_path;
@@ -2107,21 +2146,28 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, Control
p_rt->pop(); // font
pos = brk_end + 1;
+ } else if (tag == p_class) {
+ // Use a bold font when class reference tags are in their own page.
+ p_rt->push_font(doc_bold_font);
+ p_rt->add_text(tag);
+ p_rt->pop();
+
+ pos = brk_end + 1;
+
} else if (doc->class_list.has(tag)) {
- // Class reference tag such as [Node2D] or [SceneTree].
- // Use monospace font to make clickable references
- // easier to distinguish from inline code and other text.
+ // Use a monospace font for class reference tags such as [Node2D] or [SceneTree].
+
p_rt->push_font(doc_code_font);
p_rt->push_font_size(doc_code_font_size);
-
p_rt->push_color(type_color);
p_rt->push_meta("#" + tag);
p_rt->add_text(tag);
+
p_rt->pop();
p_rt->pop();
+ p_rt->pop(); // Font size
+ p_rt->pop(); // Font
- p_rt->pop(); // font size
- p_rt->pop(); // font
pos = brk_end + 1;
} else if (tag == "b") {
@@ -2252,7 +2298,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, Control
}
void EditorHelp::_add_text(const String &p_bbcode) {
- _add_text_to_rt(p_bbcode, class_desc, this);
+ _add_text_to_rt(p_bbcode, class_desc, this, edited_class);
}
Thread EditorHelp::thread;
@@ -2264,21 +2310,23 @@ void EditorHelp::_wait_for_thread() {
}
String EditorHelp::get_cache_full_path() {
- return EditorPaths::get_singleton()->get_cache_dir().path_join("editor.doc_cache");
+ return EditorPaths::get_singleton()->get_cache_dir().path_join("editor_doc_cache.res");
}
static bool first_attempt = true;
static String _compute_doc_version_hash() {
- return vformat("%d/%d/%s", ClassDB::get_api_hash(ClassDB::API_CORE), ClassDB::get_api_hash(ClassDB::API_EDITOR), _doc_data_hash);
+ uint32_t version_hash = Engine::get_singleton()->get_version_info().hash();
+ return vformat("%d/%d/%d/%s", version_hash, ClassDB::get_api_hash(ClassDB::API_CORE), ClassDB::get_api_hash(ClassDB::API_EDITOR), _doc_data_hash);
}
void EditorHelp::_load_doc_thread(void *p_udata) {
DEV_ASSERT(first_attempt);
- Ref<DocCache> cache_res = ResourceLoader::load(get_cache_full_path());
- if (cache_res.is_valid() && cache_res->get_version_hash() == _compute_doc_version_hash()) {
- for (int i = 0; i < cache_res->get_classes().size(); i++) {
- doc->add_doc(DocData::ClassDoc::from_dict(cache_res->get_classes()[i]));
+ Ref<Resource> cache_res = ResourceLoader::load(get_cache_full_path());
+ if (cache_res.is_valid() && cache_res->get_meta("version_hash", "") == _compute_doc_version_hash()) {
+ Array classes = cache_res->get_meta("classes", Array());
+ for (int i = 0; i < classes.size(); i++) {
+ doc->add_doc(DocData::ClassDoc::from_dict(classes[i]));
}
} else {
// We have to go back to the main thread to start from scratch.
@@ -2292,14 +2340,14 @@ void EditorHelp::_gen_doc_thread(void *p_udata) {
compdoc.load_compressed(_doc_data_compressed, _doc_data_compressed_size, _doc_data_uncompressed_size);
doc->merge_from(compdoc); // Ensure all is up to date.
- Ref<DocCache> cache_res;
+ Ref<Resource> cache_res;
cache_res.instantiate();
- cache_res->set_version_hash(_compute_doc_version_hash());
+ cache_res->set_meta("version_hash", _compute_doc_version_hash());
Array classes;
for (const KeyValue<String, DocData::ClassDoc> &E : doc->class_list) {
classes.push_back(DocData::ClassDoc::to_dict(E.value));
}
- cache_res->set_classes(classes);
+ cache_res->set_meta("classes", classes);
Error err = ResourceSaver::save(cache_res, get_cache_full_path(), ResourceSaver::FLAG_COMPRESS);
if (err) {
ERR_PRINT("Cannot save editor help cache (" + get_cache_full_path() + ").");
@@ -2309,9 +2357,6 @@ void EditorHelp::_gen_doc_thread(void *p_udata) {
static bool doc_gen_use_threads = true;
void EditorHelp::generate_doc(bool p_use_cache) {
- // Temporarily disable use of cache for pre-RC stabilization.
- p_use_cache = false;
-
OS::get_singleton()->benchmark_begin_measure("EditorHelp::generate_doc");
if (doc_gen_use_threads) {
// In case not the first attempt.
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_interface.cpp b/editor/editor_interface.cpp
index d9d9dc01c0..8d63082f31 100644
--- a/editor/editor_interface.cpp
+++ b/editor/editor_interface.cpp
@@ -31,6 +31,7 @@
#include "editor_interface.h"
#include "editor/editor_command_palette.h"
+#include "editor/editor_feature_profile.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_resource_preview.h"
@@ -239,6 +240,14 @@ void EditorInterface::popup_dialog_centered_clamped(Window *p_dialog, const Size
p_dialog->popup_exclusive_centered_clamped(EditorNode::get_singleton(), p_size, p_fallback_ratio);
}
+String EditorInterface::get_current_feature_profile() const {
+ return EditorFeatureProfileManager::get_singleton()->get_current_profile_name();
+}
+
+void EditorInterface::set_current_feature_profile(const String &p_profile_name) {
+ EditorFeatureProfileManager::get_singleton()->set_current_profile(p_profile_name, true);
+}
+
// Editor docks.
FileSystemDock *EditorInterface::get_file_system_dock() const {
@@ -337,6 +346,10 @@ void EditorInterface::mark_scene_as_unsaved() {
EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(EditorNode::get_editor_data().get_current_edited_scene_history_id());
}
+void EditorInterface::save_all_scenes() {
+ EditorNode::get_singleton()->save_all_scenes();
+}
+
// Scene playback.
void EditorInterface::play_main_scene() {
@@ -407,6 +420,9 @@ void EditorInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("popup_dialog_centered_ratio", "dialog", "ratio"), &EditorInterface::popup_dialog_centered_ratio, DEFVAL(0.8));
ClassDB::bind_method(D_METHOD("popup_dialog_centered_clamped", "dialog", "minsize", "fallback_ratio"), &EditorInterface::popup_dialog_centered_clamped, DEFVAL(Size2i()), DEFVAL(0.75));
+ ClassDB::bind_method(D_METHOD("get_current_feature_profile"), &EditorInterface::get_current_feature_profile);
+ ClassDB::bind_method(D_METHOD("set_current_feature_profile", "profile_name"), &EditorInterface::set_current_feature_profile);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distraction_free_mode"), "set_distraction_free_mode", "is_distraction_free_mode_enabled");
// Editor docks.
@@ -434,6 +450,7 @@ void EditorInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("save_scene"), &EditorInterface::save_scene);
ClassDB::bind_method(D_METHOD("save_scene_as", "path", "with_preview"), &EditorInterface::save_scene_as, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("save_all_scenes"), &EditorInterface::save_all_scenes);
ClassDB::bind_method(D_METHOD("mark_scene_as_unsaved"), &EditorInterface::mark_scene_as_unsaved);
diff --git a/editor/editor_interface.h b/editor/editor_interface.h
index f7e8cf8d4c..2ac1336202 100644
--- a/editor/editor_interface.h
+++ b/editor/editor_interface.h
@@ -104,6 +104,9 @@ public:
void popup_dialog_centered_ratio(Window *p_dialog, float p_ratio = 0.8);
void popup_dialog_centered_clamped(Window *p_dialog, const Size2i &p_size = Size2i(), float p_fallback_ratio = 0.75);
+ String get_current_feature_profile() const;
+ void set_current_feature_profile(const String &p_profile_name);
+
// Editor docks.
FileSystemDock *get_file_system_dock() const;
@@ -130,6 +133,7 @@ public:
Error save_scene();
void save_scene_as(const String &p_scene, bool p_with_preview = true);
void mark_scene_as_unsaved();
+ void save_all_scenes();
// Scene playback.
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 0c74468816..a58e17724b 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"
@@ -320,7 +322,7 @@ void EditorNode::_update_scene_tabs() {
scene_tabs->set_current_tab(editor_data.get_edited_scene());
}
- const Size2 add_button_size = Size2(0, scene_tabs->get_size().y);
+ const Size2 add_button_size = Size2(scene_tab_add->get_size().x, scene_tabs->get_size().y);
if (scene_tabs->get_offset_buttons_visible()) {
// Move the add button to a fixed position.
if (scene_tab_add->get_parent() == scene_tabs) {
@@ -343,7 +345,7 @@ void EditorNode::_update_scene_tabs() {
Rect2 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1);
int hsep = scene_tabs->get_theme_constant(SNAME("h_separation"));
if (scene_tabs->is_layout_rtl()) {
- scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - scene_tab_add->get_size().x - hsep, last_tab.position.y), add_button_size));
+ scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - add_button_size.x - hsep, last_tab.position.y), add_button_size));
} else {
scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y), add_button_size));
}
@@ -1411,7 +1413,7 @@ void EditorNode::_dialog_display_load_error(String p_file, Error p_error) {
show_accept(vformat(TTR("Scene file '%s' appears to be invalid/corrupt."), p_file.get_file()), TTR("OK"));
} break;
case ERR_FILE_NOT_FOUND: {
- show_accept(vformat(TTR("Missing file '%s' or one its dependencies."), p_file.get_file()), TTR("OK"));
+ show_accept(vformat(TTR("Missing file '%s' or one of its dependencies."), p_file.get_file()), TTR("OK"));
} break;
default: {
show_accept(vformat(TTR("Error while loading file '%s'."), p_file.get_file()), TTR("OK"));
@@ -1982,6 +1984,9 @@ void EditorNode::_dialog_action(String p_file) {
if (scene_idx != -1) {
_discard_changes();
+ } else {
+ // Update the path of the edited scene to ensure later do/undo action history matches.
+ editor_data.set_scene_path(editor_data.get_edited_scene(), p_file);
}
}
@@ -2775,6 +2780,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 +2801,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 +3061,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 +3230,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 +3366,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 +3416,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);
@@ -3574,7 +3620,9 @@ void EditorNode::set_current_scene(int p_idx) {
_update_title();
_update_scene_tabs();
- call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // Do after everything else is done setting up.
+ if (tabs_to_close.is_empty()) {
+ call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // Do after everything else is done setting up.
+ }
}
void EditorNode::setup_color_picker(ColorPicker *p_picker) {
@@ -4234,7 +4282,7 @@ void EditorNode::_pick_main_scene_custom_action(const String &p_custom_action_na
}
}
-Ref<Texture2D> EditorNode::_get_class_or_script_icon(const String &p_class, const Ref<Script> &p_script, const String &p_fallback) {
+Ref<Texture2D> EditorNode::_get_class_or_script_icon(const String &p_class, const Ref<Script> &p_script, const String &p_fallback, bool p_fallback_script_to_theme) {
ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty.");
EditorData &ed = EditorNode::get_editor_data();
@@ -4244,6 +4292,16 @@ Ref<Texture2D> EditorNode::_get_class_or_script_icon(const String &p_class, cons
if (script_icon.is_valid()) {
return script_icon;
}
+
+ if (p_fallback_script_to_theme) {
+ // Look for the base type in the editor theme.
+ // This is only relevant for built-in classes.
+ String base_type;
+ p_script->get_language()->get_global_class_name(p_script->get_path(), &base_type);
+ if (gui_base && gui_base->has_theme_icon(base_type, SNAME("EditorIcons"))) {
+ return gui_base->get_theme_icon(base_type, SNAME("EditorIcons"));
+ }
+ }
}
// Script was not valid or didn't yield any useful values, try the class name
@@ -4296,7 +4354,7 @@ Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p
scr = EditorNode::get_editor_data().script_class_load_script(p_class);
}
- return _get_class_or_script_icon(p_class, scr, p_fallback);
+ return _get_class_or_script_icon(p_class, scr, p_fallback, true);
}
bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringName &p_class) {
@@ -4376,6 +4434,9 @@ String EditorNode::_get_system_info() const {
godot_version += " " + hash;
}
+#ifdef LINUXBSD_ENABLED
+ const String display_server = OS::get_singleton()->get_environment("XDG_SESSION_TYPE").capitalize().replace(" ", ""); // `replace` is necessary, because `capitalize` introduces a whitespace between "x" and "11".
+#endif // LINUXBSD_ENABLED
String driver_name = GLOBAL_GET("rendering/rendering_device/driver");
String rendering_method = GLOBAL_GET("rendering/renderer/rendering_method");
@@ -4407,17 +4468,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.
@@ -4428,6 +4490,11 @@ String EditorNode::_get_system_info() const {
} else {
info.push_back(distribution_name);
}
+#ifdef LINUXBSD_ENABLED
+ if (!display_server.is_empty()) {
+ info.push_back(display_server);
+ }
+#endif // LINUXBSD_ENABLED
info.push_back(vformat("%s (%s)", driver_name, rendering_method));
String graphics;
@@ -4923,10 +4990,7 @@ void EditorNode::_save_open_scenes_to_config(Ref<ConfigFile> p_layout) {
p_layout->set_value(EDITOR_NODE_CONFIG_SECTION, "open_scenes", scenes);
String currently_edited_scene_path = editor_data.get_scene_path(editor_data.get_edited_scene());
- // Don't save a bad path to the config.
- if (!currently_edited_scene_path.is_empty()) {
- p_layout->set_value(EDITOR_NODE_CONFIG_SECTION, "current_scene", currently_edited_scene_path);
- }
+ p_layout->set_value(EDITOR_NODE_CONFIG_SECTION, "current_scene", currently_edited_scene_path);
}
void EditorNode::save_editor_layout_delayed() {
@@ -5275,21 +5339,29 @@ void EditorNode::_save_central_editor_layout_to_config(Ref<ConfigFile> p_config_
void EditorNode::_load_central_editor_layout_from_config(Ref<ConfigFile> p_config_file) {
// Bottom panel.
- if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "center_split_offset")) {
- int center_split_offset = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "center_split_offset");
- center_split->set_split_offset(center_split_offset);
- }
-
+ bool has_active_tab = false;
if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item")) {
int selected_bottom_panel_item_idx = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item");
if (selected_bottom_panel_item_idx >= 0 && selected_bottom_panel_item_idx < bottom_panel_items.size()) {
// Make sure we don't try to open contextual editors which are not enabled in the current context.
if (bottom_panel_items[selected_bottom_panel_item_idx].button->is_visible()) {
_bottom_panel_switch(true, selected_bottom_panel_item_idx);
+ has_active_tab = true;
}
}
}
+ if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "center_split_offset")) {
+ int center_split_offset = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "center_split_offset");
+ center_split->set_split_offset(center_split_offset);
+
+ // If there is no active tab we need to collapse the panel.
+ if (!has_active_tab) {
+ bottom_panel_items[0].control->show(); // _bottom_panel_switch() can collapse only visible tabs.
+ _bottom_panel_switch(false, 0);
+ }
+ }
+
// Debugger tab.
if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "selected_default_debugger_tab_idx")) {
@@ -5327,7 +5399,9 @@ void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout) {
if (p_layout->has_section_key(EDITOR_NODE_CONFIG_SECTION, "current_scene")) {
String current_scene = p_layout->get_value(EDITOR_NODE_CONFIG_SECTION, "current_scene");
int current_scene_idx = scenes.find(current_scene);
- set_current_scene(current_scene_idx);
+ if (current_scene_idx >= 0) {
+ set_current_scene(current_scene_idx);
+ }
}
save_editor_layout_delayed();
@@ -5537,19 +5611,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();
@@ -5589,11 +5680,17 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
if (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed()) {
_scene_tab_closed(scene_tabs->get_hovered_tab());
}
- } else {
- if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) {
+ } else if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) {
+ int tab_buttons = 0;
+ if (scene_tabs->get_offset_buttons_visible()) {
+ tab_buttons = theme->get_icon(SNAME("increment"), SNAME("TabBar"))->get_width() + theme->get_icon(SNAME("decrement"), SNAME("TabBar"))->get_width();
+ }
+
+ if ((gui_base->is_layout_rtl() && mb->get_position().x > tab_buttons) || (!gui_base->is_layout_rtl() && mb->get_position().x < scene_tabs->get_size().width - tab_buttons)) {
_menu_option_confirm(FILE_NEW_SCENE, true);
}
}
+
if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
// Context menu.
scene_tabs_context_menu->clear();
@@ -6732,7 +6829,8 @@ EditorNode::EditorNode() {
// No scripting by default if in editor.
ScriptServer::set_scripting_enabled(false);
- EditorHelp::generate_doc(); // Before any editor classes are created.
+ EditorSettings::ensure_class_registered();
+ EditorHelp::generate_doc();
SceneState::set_disable_placeholders(true);
ResourceLoader::clear_translation_remaps(); // Using no remaps if in editor.
ResourceLoader::clear_path_remaps();
@@ -7119,7 +7217,7 @@ EditorNode::EditorNode() {
dock_select->set_v_size_flags(Control::SIZE_EXPAND_FILL);
dock_vb->add_child(dock_select);
- if (!SceneTree::get_singleton()->get_root()->is_embedding_subwindows() && EDITOR_GET("interface/multi_window/enable")) {
+ if (!SceneTree::get_singleton()->get_root()->is_embedding_subwindows() && !EDITOR_GET("interface/editor/single_window_mode") && EDITOR_GET("interface/multi_window/enable")) {
dock_float = memnew(Button);
dock_float->set_icon(theme->get_icon("MakeFloating", "EditorIcons"));
dock_float->set_text(TTR("Make Floating"));
@@ -7747,6 +7845,7 @@ EditorNode::EditorNode() {
log = memnew(EditorLog);
Button *output_button = add_bottom_panel_item(TTR("Output"), log);
+ output_button->set_theme_type_variation("BottomPanelButton");
log->set_tool_button(output_button);
center_split->connect("resized", callable_mp(this, &EditorNode::_vp_resized));
@@ -8109,6 +8208,9 @@ EditorNode::~EditorNode() {
memdelete(progress_hb);
EditorSettings::destroy();
+
+ GDExtensionEditorPlugins::editor_node_add_plugin = nullptr;
+ GDExtensionEditorPlugins::editor_node_remove_plugin = nullptr;
}
/*
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 65f85a76c9..86b4847e5b 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;
@@ -687,7 +688,7 @@ private:
void _feature_profile_changed();
bool _is_class_editor_disabled_by_feature_profile(const StringName &p_class);
- Ref<Texture2D> _get_class_or_script_icon(const String &p_class, const Ref<Script> &p_script, const String &p_fallback = "Object");
+ Ref<Texture2D> _get_class_or_script_icon(const String &p_class, const Ref<Script> &p_script, const String &p_fallback = "Object", bool p_fallback_script_to_theme = false);
void _pick_main_scene_custom_action(const String &p_custom_action_name);
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 04a019a407..77d6ec9ab2 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -621,7 +621,7 @@ void EditorPropertyClassName::update_property() {
}
void EditorPropertyClassName::_property_selected() {
- dialog->popup_create(true);
+ dialog->popup_create(true, true, get_edited_property_value(), get_edited_property());
}
void EditorPropertyClassName::_dialog_created() {
@@ -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);
@@ -3622,7 +3622,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
return editor;
} else if (p_hint == PROPERTY_HINT_TYPE_STRING) {
EditorPropertyClassName *editor = memnew(EditorPropertyClassName);
- editor->setup("Object", p_hint_text);
+ editor->setup(p_hint_text, p_hint_text);
return editor;
} else if (p_hint == PROPERTY_HINT_LOCALE_ID) {
EditorPropertyLocale *editor = memnew(EditorPropertyLocale);
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 8634b94858..285be7e7ae 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -597,7 +597,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// Completion
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "text_editor/completion/idle_parse_delay", 2.0, "0.1,10,0.01")
_initial_set("text_editor/completion/auto_brace_complete", true);
- EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "text_editor/completion/code_complete_delay", 0.3, "0.01,5,0.01")
+ _initial_set("text_editor/completion/code_complete_enabled", true);
+ EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "text_editor/completion/code_complete_delay", 0.3, "0.01,5,0.01,or_greater")
_initial_set("text_editor/completion/put_callhint_tooltip_below_current_line", true);
_initial_set("text_editor/completion/complete_file_paths", true);
_initial_set("text_editor/completion/add_type_hints", false);
@@ -679,14 +680,15 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/2d/grid_color", Color(1.0, 1.0, 1.0, 0.07));
_initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8));
_initial_set("editors/2d/smart_snapping_line_color", Color(0.9, 0.1, 0.1));
- _initial_set("editors/2d/bone_width", 5);
+ EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/2d/bone_width", 5.0, "0.01,20,0.01,or_greater")
_initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.7));
_initial_set("editors/2d/bone_color2", Color(0.6, 0.6, 0.6, 0.7));
_initial_set("editors/2d/bone_selected_color", Color(0.9, 0.45, 0.45, 0.7));
_initial_set("editors/2d/bone_ik_color", Color(0.9, 0.9, 0.45, 0.7));
_initial_set("editors/2d/bone_outline_color", Color(0.35, 0.35, 0.35, 0.5));
- _initial_set("editors/2d/bone_outline_size", 2);
+ EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/2d/bone_outline_size", 2.0, "0.01,8,0.01,or_greater")
_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.
@@ -707,7 +709,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// Animation
_initial_set("editors/animation/autorename_animation_tracks", true);
- _initial_set("editors/animation/confirm_insert_track", true);
_initial_set("editors/animation/default_create_bezier_tracks", false);
_initial_set("editors/animation/default_create_reset_tracks", true);
_initial_set("editors/animation/onion_layers_past_color", Color(1, 0, 0));
@@ -877,6 +878,13 @@ EditorSettings *EditorSettings::get_singleton() {
return singleton.ptr();
}
+void EditorSettings::ensure_class_registered() {
+ ClassDB::APIType prev_api = ClassDB::get_current_api();
+ ClassDB::set_current_api(ClassDB::API_EDITOR);
+ GDREGISTER_CLASS(EditorSettings); // Otherwise it can't be unserialized.
+ ClassDB::set_current_api(prev_api);
+}
+
void EditorSettings::create() {
// IMPORTANT: create() *must* create a valid EditorSettings singleton,
// as the rest of the engine code will assume it. As such, it should never
@@ -887,7 +895,7 @@ void EditorSettings::create() {
return;
}
- GDREGISTER_CLASS(EditorSettings); // Otherwise it can't be unserialized.
+ ensure_class_registered();
String config_file_path;
Ref<ConfigFile> extra_config = memnew(ConfigFile);
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index 660a9501a2..55ac6c5a15 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -117,6 +117,7 @@ public:
static EditorSettings *get_singleton();
+ static void ensure_class_registered();
static void create();
void setup_language();
void setup_network();
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 2ff53dd9f1..3302b1dc41 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
@@ -244,8 +248,7 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float
// Generating upsampled icons is slower, and the benefit is hardly visible
// with integer editor scales.
const bool upsample = !Math::is_equal_approx(Math::round(p_scale), p_scale);
- ImageLoaderSVG img_loader;
- Error err = img_loader.create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_colors);
+ Error err = ImageLoaderSVG::create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_colors);
ERR_FAIL_COND_V_MSG(err != OK, Ref<ImageTexture>(), "Failed generating icon, unsupported or invalid SVG data in editor theme.");
if (p_saturation != 1.0) {
img->adjust_bcs(1.0, 1.0, p_saturation);
@@ -1276,6 +1279,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("h_separation", "Tree", 6 * EDSCALE);
theme->set_constant("guide_width", "Tree", border_width);
theme->set_constant("item_margin", "Tree", 3 * default_margin_size * EDSCALE);
+ theme->set_constant("inner_item_margin_bottom", "Tree", (default_margin_size + extra_spacing) * EDSCALE);
+ theme->set_constant("inner_item_margin_left", "Tree", (default_margin_size + extra_spacing) * EDSCALE);
+ theme->set_constant("inner_item_margin_right", "Tree", (default_margin_size + extra_spacing) * EDSCALE);
+ theme->set_constant("inner_item_margin_top", "Tree", (default_margin_size + extra_spacing) * EDSCALE);
theme->set_constant("button_margin", "Tree", default_margin_size * EDSCALE);
theme->set_constant("scroll_border", "Tree", 40 * EDSCALE);
theme->set_constant("scroll_speed", "Tree", 12);
@@ -1446,6 +1453,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("panel", "TabContainer", style_content_panel);
// Bottom panel.
+ theme->set_type_variation("BottomPanelButton", "Button");
+ // Add separation for the warning/error icon.
+ theme->set_constant("h_separation", "BottomPanelButton", 6 * EDSCALE);
Ref<StyleBoxFlat> style_bottom_panel = style_content_panel->duplicate();
style_bottom_panel->set_corner_radius_all(corner_radius * EDSCALE);
theme->set_stylebox("BottomPanel", "EditorStyles", style_bottom_panel);
@@ -1602,6 +1612,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")));
@@ -1785,7 +1796,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("outline_size", "ProgressBar", 0);
// GraphEdit
- theme->set_stylebox("bg", "GraphEdit", style_tree_bg);
+ theme->set_stylebox("panel", "GraphEdit", style_tree_bg);
if (dark_theme) {
theme->set_color("grid_major", "GraphEdit", Color(1.0, 1.0, 1.0, 0.15));
theme->set_color("grid_minor", "GraphEdit", Color(1.0, 1.0, 1.0, 0.07));
@@ -1796,18 +1807,20 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("selection_fill", "GraphEdit", theme->get_color(SNAME("box_selection_fill_color"), SNAME("Editor")));
theme->set_color("selection_stroke", "GraphEdit", theme->get_color(SNAME("box_selection_stroke_color"), SNAME("Editor")));
theme->set_color("activity", "GraphEdit", accent_color);
- theme->set_icon("minus", "GraphEdit", theme->get_icon(SNAME("ZoomLess"), SNAME("EditorIcons")));
- theme->set_icon("more", "GraphEdit", theme->get_icon(SNAME("ZoomMore"), SNAME("EditorIcons")));
- theme->set_icon("reset", "GraphEdit", theme->get_icon(SNAME("ZoomReset"), SNAME("EditorIcons")));
- theme->set_icon("snap", "GraphEdit", theme->get_icon(SNAME("SnapGrid"), SNAME("EditorIcons")));
- theme->set_icon("minimap", "GraphEdit", theme->get_icon(SNAME("GridMinimap"), SNAME("EditorIcons")));
+
+ theme->set_icon("zoom_out", "GraphEdit", theme->get_icon(SNAME("ZoomLess"), SNAME("EditorIcons")));
+ theme->set_icon("zoom_in", "GraphEdit", theme->get_icon(SNAME("ZoomMore"), SNAME("EditorIcons")));
+ theme->set_icon("zoom_reset", "GraphEdit", theme->get_icon(SNAME("ZoomReset"), SNAME("EditorIcons")));
+ theme->set_icon("grid_toggle", "GraphEdit", theme->get_icon(SNAME("GridToggle"), SNAME("EditorIcons")));
+ theme->set_icon("minimap_toggle", "GraphEdit", theme->get_icon(SNAME("GridMinimap"), SNAME("EditorIcons")));
+ theme->set_icon("snapping_toggle", "GraphEdit", theme->get_icon(SNAME("SnapGrid"), SNAME("EditorIcons")));
theme->set_icon("layout", "GraphEdit", theme->get_icon(SNAME("GridLayout"), SNAME("EditorIcons")));
// GraphEditMinimap
Ref<StyleBoxFlat> style_minimap_bg = make_flat_stylebox(dark_color_1, 0, 0, 0, 0);
style_minimap_bg->set_border_color(dark_color_3);
style_minimap_bg->set_border_width_all(1);
- theme->set_stylebox("bg", "GraphEditMinimap", style_minimap_bg);
+ theme->set_stylebox("panel", "GraphEditMinimap", style_minimap_bg);
Ref<StyleBoxFlat> style_minimap_camera;
Ref<StyleBoxFlat> style_minimap_node;
@@ -1887,8 +1900,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("frame", "GraphNode", graphsb);
theme->set_stylebox("selected_frame", "GraphNode", graphsbselected);
- theme->set_stylebox("comment", "GraphNode", graphsbcomment);
- theme->set_stylebox("comment_focus", "GraphNode", graphsbcommentselected);
theme->set_stylebox("breakpoint", "GraphNode", graphsbbreakpoint);
theme->set_stylebox("position", "GraphNode", graphsbposition);
theme->set_stylebox("slot", "GraphNode", graphsbslot);
@@ -1910,7 +1921,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("close", "GraphNode", theme->get_icon(SNAME("GuiCloseCustomizable"), SNAME("EditorIcons")));
theme->set_icon("resizer", "GraphNode", theme->get_icon(SNAME("GuiResizer"), SNAME("EditorIcons")));
- theme->set_icon("port", "GraphNode", theme->get_icon(SNAME("GuiGraphNodePort"), SNAME("EditorIcons")));
+ Ref<ImageTexture> port_icon = theme->get_icon(SNAME("GuiGraphNodePort"), SNAME("EditorIcons"));
+ // The true size is 24x24 This is necessary for sharp port icons at high zoom levels in GraphEdit (up to ~200%).
+ port_icon->set_size_override(Size2(12, 12));
+ theme->set_icon("port", "GraphNode", port_icon);
theme->set_font("title_font", "GraphNode", theme->get_font(SNAME("main_bold_msdf"), SNAME("EditorFonts")));
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_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index a7ddb984e0..72933517d7 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -823,7 +823,7 @@ void EditorFileDialog::update_file_list() {
}
} else if (!dir_access->current_is_hidden()) {
String full_path = cdir == "res://" ? item : dir_access->get_current_dir() + "/" + item;
- if (dir_access->current_is_dir() && (!EditorFileSystem::_should_skip_directory(full_path) || Engine::get_singleton()->is_project_manager_hint())) {
+ if (dir_access->current_is_dir() && (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path))) {
dirs.push_back(item);
} else {
files.push_back(item);
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/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index 41f81a3f60..fbe167814d 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -1208,8 +1208,11 @@ Variant SceneTreeEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from
if (i < list_max) {
HBoxContainer *hb = memnew(HBoxContainer);
TextureRect *tf = memnew(TextureRect);
+ int icon_size = get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"));
+ tf->set_custom_minimum_size(Size2(icon_size, icon_size));
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
+ tf->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
tf->set_texture(icons[i]);
- tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
hb->add_child(tf);
Label *label = memnew(Label(selected_nodes[i]->get_name()));
hb->add_child(label);
diff --git a/editor/icons/AnimatedSprite2D.svg b/editor/icons/AnimatedSprite2D.svg
index b22aeadeb6..d0a3f248a5 100644
--- a/editor/icons/AnimatedSprite2D.svg
+++ b/editor/icons/AnimatedSprite2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#8da5f3"><path d="m7 0c-1.108 0-2 .89199-2 2h7c1.108 0 2 .89199 2 2v6c1.108 0 2-.89199 2-2v-6c0-1.108-.89199-2-2-2z" fill-opacity=".39216"/><path d="m5 2c-1.108 0-2 .89199-2 2h7c1.108 0 2 .89199 2 2v7c1.108 0 2-.89199 2-2v-7c0-1.108-.89199-2-2-2z" fill-opacity=".58824"/><path d="m3 4c-1.108 0-2 .89199-2 2v7c0 1.108.89199 2 2 2h7c1.108 0 2-.89199 2-2v-7c0-1.108-.89199-2-2-2zm0 4c.554 0 1 .446 1 1v1c0 .554-.446 1-1 1s-1-.446-1-1v-1c0-.554.446-1 1-1zm7 0c.554 0 1 .446 1 1v1c0 .554-.446 1-1 1s-1-.446-1-1v-1c0-.554.446-1 1-1zm-6 4h5a2.5 2 0 0 1 -1.25 1.7324 2.5 2 0 0 1 -2.5 0 2.5 2 0 0 1 -1.25-1.7324z"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#8da5f3"><path d="M7 0a2 2 0 0 0-2 2h7a2 2 0 0 1 2 2v6a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z" fill-opacity=".392"/><path d="M5 2a2 2 0 0 0-2 2h7a2 2 0 0 1 2 2v7a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z" fill-opacity=".588"/><path d="M3 4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zM2 9a1 1 0 0 1 2 0v1a1 1 0 0 1-2 0zm7 0a1 1 0 0 1 2 0v1a1 1 0 0 1-2 0zm0 3a2.5 2 0 0 1-5 0z"/></g></svg>
diff --git a/editor/icons/AnimatedSprite3D.svg b/editor/icons/AnimatedSprite3D.svg
index 99520a3bc2..59ba788920 100644
--- a/editor/icons/AnimatedSprite3D.svg
+++ b/editor/icons/AnimatedSprite3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m7 0c-1.108 0-2 .89199-2 2h7c1.108 0 2 .89199 2 2v6c1.108 0 2-.89199 2-2v-6c0-1.108-.89199-2-2-2z" fill-opacity=".39216"/><path d="m5 2c-1.108 0-2 .89199-2 2h7c1.108 0 2 .89199 2 2v7c1.108 0 2-.89199 2-2v-7c0-1.108-.89199-2-2-2z" fill-opacity=".58824"/><path d="m3 4c-1.108 0-2 .89199-2 2v7c0 1.108.89199 2 2 2h7c1.108 0 2-.89199 2-2v-7c0-1.108-.89199-2-2-2zm0 4c.554 0 1 .446 1 1v1c0 .554-.446 1-1 1s-1-.446-1-1v-1c0-.554.446-1 1-1zm7 0c.554 0 1 .446 1 1v1c0 .554-.446 1-1 1s-1-.446-1-1v-1c0-.554.446-1 1-1zm-6 4h5a2.5 2 0 0 1 -1.25 1.7324 2.5 2 0 0 1 -2.5 0 2.5 2 0 0 1 -1.25-1.7324z"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="M7 0a2 2 0 0 0-2 2h7a2 2 0 0 1 2 2v6a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z" fill-opacity=".392"/><path d="M5 2a2 2 0 0 0-2 2h7a2 2 0 0 1 2 2v7a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z" fill-opacity=".588"/><path d="M3 4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zM2 9a1 1 0 0 1 2 0v1a1 1 0 0 1-2 0zm7 0a1 1 0 0 1 2 0v1a1 1 0 0 1-2 0zm0 3a2.5 2 0 0 1-5 0z"/></g></svg>
diff --git a/editor/icons/AssetLib.svg b/editor/icons/AssetLib.svg
index 22307efde3..6463c1a3b5 100644
--- a/editor/icons/AssetLib.svg
+++ b/editor/icons/AssetLib.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#e0e0e0" stroke-linejoin="round" stroke-width="2"><path d="m8 1v9l4-4"/><path d="m8 10-4-4"/><path d="m2 10v4h12v-4" stroke-linecap="round"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#e0e0e0" stroke-linejoin="round" stroke-width="2"><path d="M8 1v9l4-4m-4 4L4 6"/><path d="M2 10v4h12v-4" stroke-linecap="round"/></g></svg>
diff --git a/editor/icons/AutoKey.svg b/editor/icons/AutoKey.svg
index 877b00722f..5af089c4a8 100644
--- a/editor/icons/AutoKey.svg
+++ b/editor/icons/AutoKey.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><circle cx="8" cy="5" r="4"/><path d="m11 13c0 1.6569 1.3431 3 3 3h1v-2h-1c-.55228-.00001-.99999-.44772-1-1 .00001-.55228.44772-.99999 1-1h1v-2h-1c-1.6569 0-3 1.3431-3 3z"/><path d="m4 10c-1.6569 0-3 1.3431-3 3v3h2v-3c.0000096-.5523.44772-1 1-1h1v-2z"/><path d="m8 10c-3 0-3 3-3 3s0 3 3 3h1v-2h-1s-1 0-1-1h3 1s0-3-3-3zm-1 1h2v1h-2z"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><circle cx="8" cy="5" r="4"/><path d="M11 13a3 3 0 0 0 3 3h1v-2h-1a1 1 0 0 1 0-2h1v-2h-1a3 3 0 0 0-3 3zm-7-3a3 3 0 0 0-3 3v3h2v-3a1 1 0 0 1 1-1h1v-2zm4 6h1v-2H8a1 1 0 0 1-1-1h4a3 3 0 1 0-3 3zm-1-4a1.08 1.08 0 0 1 2 0z"/></g></svg>
diff --git a/editor/icons/Bone.svg b/editor/icons/Bone.svg
index 46b068c0ec..16c232c71e 100644
--- a/editor/icons/Bone.svg
+++ b/editor/icons/Bone.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M10.478 1a2.466 2.466 0 0 0-2.094 3.824l-3.56 3.56a2.466 2.466 0 1 0-1.705 4.496 2.466 2.466 0 1 0 4.496-1.705l3.56-3.56a2.466 2.466 0 1 0 1.705-4.496A2.466 2.466 0 0 0 10.478 1z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.824 8.384a2.466 2.466 0 1 0-1.705 4.496 2.466 2.466 0 1 0 4.496-1.705l3.56-3.56a2.466 2.466 0 1 0 1.705-4.496 2.466 2.466 0 1 0-4.496 1.705z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Bone2D.svg b/editor/icons/Bone2D.svg
index 31515216c9..efa1d9d2cf 100644
--- a/editor/icons/Bone2D.svg
+++ b/editor/icons/Bone2D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M10.478 1a2.466 2.466 0 0 0-2.094 3.824l-3.56 3.56a2.466 2.466 0 1 0-1.705 4.496 2.466 2.466 0 1 0 4.496-1.705l3.56-3.56a2.466 2.466 0 1 0 1.705-4.496A2.466 2.466 0 0 0 10.478 1z" fill="#8da5f3"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.824 8.384a2.466 2.466 0 1 0-1.705 4.496 2.466 2.466 0 1 0 4.496-1.705l3.56-3.56a2.466 2.466 0 1 0 1.705-4.496 2.466 2.466 0 1 0-4.496 1.705z" fill="#8da5f3"/></svg>
diff --git a/editor/icons/BoneAttachment3D.svg b/editor/icons/BoneAttachment3D.svg
index 37bfd159df..2cc0adb906 100644
--- a/editor/icons/BoneAttachment3D.svg
+++ b/editor/icons/BoneAttachment3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M10.478 1a2.466 2.466 0 0 0-2.094 3.824l-3.56 3.56a2.466 2.466 0 1 0-1.705 4.496 2.466 2.466 0 1 0 4.496-1.705l3.56-3.56a2.466 2.466 0 1 0 1.705-4.496A2.466 2.466 0 0 0 10.478 1z" fill="#fc7f7f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.824 8.384a2.466 2.466 0 1 0-1.705 4.496 2.466 2.466 0 1 0 4.496-1.705l3.56-3.56a2.466 2.466 0 1 0 1.705-4.496 2.466 2.466 0 1 0-4.496 1.705z" fill="#fc7f7f"/></svg>
diff --git a/editor/icons/ClassList.svg b/editor/icons/ClassList.svg
index 11713b125a..0316dfc942 100644
--- a/editor/icons/ClassList.svg
+++ b/editor/icons/ClassList.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1v1h-5v1h2v10h1 5v1h6v-3h-6v1h-5v-4h5v1h6v-3h-6v1h-5v-4h2v1h6v-3z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1v1h-5v1h2v10h6v1h6v-3h-6v1h-5v-4h5v1h6v-3h-6v1h-5v-4h2v1h6v-3z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/CopyNodePath.svg b/editor/icons/CopyNodePath.svg
index 68952bd2cf..457982bf14 100644
--- a/editor/icons/CopyNodePath.svg
+++ b/editor/icons/CopyNodePath.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><circle cx="3" cy="12"/><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V6l-5-5zm1 2h6v3c0 .554.446 1 1 1h3v6H3zm3 5-2 4h2l2-4zm4 0-2 4h2l2-4z"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V6l-5-5zm1 2h6v3a1 1 0 0 0 1 1h3v6H3zm3 5-2 4h2l2-4zm4 0-2 4h2l2-4z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/CryptoKey.svg b/editor/icons/CryptoKey.svg
index c5d1af1d23..e4faefabae 100644
--- a/editor/icons/CryptoKey.svg
+++ b/editor/icons/CryptoKey.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 4.233 4.233" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2.397.34-.374.373-.375.374v.375l.188.187-1.497 1.496v.375l.374.374h.374l.187-.188.282-.092.092-.282.282-.093.093-.28.094-.28.28-.095.187-.187.187.187h.374l.375-.375.373-.373.001-.374-1.122-1.122zm.374.858a.264.264 0 1 1 .002.528.264.264 0 0 1 -.002-.528z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 4.233 4.233" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2.397.34-.749.747v.375l.188.187L.339 3.145v.375l.374.374h.374l.187-.188.282-.092.092-.282.282-.093.187-.56.28-.095.187-.187.187.187h.374l.748-.748.001-.374L2.772.34zm.374.858a.264.264 0 1 1 .002.528.264.264 0 0 1-.002-.528z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/EditorPositionUnselected.svg b/editor/icons/EditorPositionUnselected.svg
index 30aaa77659..e99bafd258 100644
--- a/editor/icons/EditorPositionUnselected.svg
+++ b/editor/icons/EditorPositionUnselected.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 0v4.4199a4.2662 4.0576 0 0 0 -1.709 1.5801h-4.291v4h4.2949a4.2662 4.0576 0 0 0 1.7051 1.582v4.418h4v-4.4199a4.2662 4.0576 0 0 0 1.709-1.5801h4.291v-4h-4.2949a4.2662 4.0576 0 0 0 -1.7051-1.582v-4.418z" fill-opacity=".41077"/><path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1-.11914 4.2662 4.0576 0 0 1 1 .11914v-3.0605zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199-2.9199 2.9201 2.9201 0 0 0 -2.9199-2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -.13477-1 4.2662 4.0576 0 0 1 .13672-1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 .13477 1 4.2662 4.0576 0 0 1 -.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 .11914 4.2662 4.0576 0 0 1 -1-.11914z" fill="#d6d6d6"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M6 0v4.42A4.266 4.058 0 0 0 4.291 6H0v4h4.295A4.266 4.058 0 0 0 6 11.582V16h4v-4.42A4.266 4.058 0 0 0 11.709 10H16V6h-4.295A4.266 4.058 0 0 0 10 4.418V0z" fill-opacity=".411"/><path d="M7 1v3.06a4.266 4.058 0 0 1 2 0V1zm1 4.08a2.92 2.92 0 0 0 0 5.84 2.92 2.92 0 0 0 0-5.84zM1 7v2h2.87a4.266 4.058 0 0 1 .001-2H1zm11.131 0a4.266 4.058 0 0 1-.002 2H15V7h-2.869zm-5.13 4.94V15h2v-3.06a4.266 4.058 0 0 1-2 0z" fill="#d6d6d6"/></svg>
diff --git a/editor/icons/Enum.svg b/editor/icons/Enum.svg
index 45d2c7e24c..03b9b4ae5f 100644
--- a/editor/icons/Enum.svg
+++ b/editor/icons/Enum.svg
@@ -1 +1 @@
-<svg height="12" viewBox="0 0 16 12" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 2c-.5304.0000801-1.0391.21085-1.4141.58594-.37509.37501-.58586.88366-.58594 1.4141v1c-.2652.0000401-.51953.10542-.70703.29297-.18755.18751-.29293.44183-.29297.70703.00004008.2652.10542.51953.29297.70703.18751.18755.44183.29293.70703.29297v1c.0000801.5304.21085 1.0391.58594 1.4141.37501.37509.88366.58586 1.4141.58594h1v-2h-1v-4h1v-2zm3 0v8h4v-2h-2v-1h2v-2h-2v-1h2v-2zm6 0v2h1v4h-1v2h1c.5304-.0000803 1.0391-.21085 1.4141-.58594.37509-.37501.58586-.88366.58594-1.4141v-1c.2652-.0000401.51953-.10542.70703-.29297.18755-.18751.29293-.44183.29297-.70703-.00004-.2652-.10542-.51953-.29297-.70703-.1875-.18755-.44183-.29293-.70703-.29297v-1c-.00008-.5304-.21085-1.0391-.58594-1.4141-.37501-.37509-.88366-.58586-1.4141-.58594z" fill="#e0e0e0"/></svg>
+<svg height="12" viewBox="0 0 16 12" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 2a2 2 0 0 0-2 2v1a1 1 0 0 0 0 2v1a2 2 0 0 0 2 2h1V8H3V4h1V2zm3 0v8h4V8H8V7h2V5H8V4h2V2zm6 0v2h1v4h-1v2h1a2 2 0 0 0 2-2V7a1 1 0 0 0 0-2V4a2 2 0 0 0-2-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/ExpandBottomDock.svg b/editor/icons/ExpandBottomDock.svg
index 636d4f8b83..61120ec14f 100644
--- a/editor/icons/ExpandBottomDock.svg
+++ b/editor/icons/ExpandBottomDock.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m4.2130251 4.516057-3.5355339 3.5355339h2.5356849v4.9737171h1.9998394v-4.9737171h2.5356849l-3.5355339-3.5355339z"/><path d="m11.907306 4.6119359-3.5355342 3.5355339h2.5356852v4.9737172h1.999839v-4.9737172h2.535685l-3.535534-3.5355339z"/><path d="m1.288136 1.370074h14v1.830509h-14z"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4 4.5 1 8h2v5h2V8h2zm8 0L9 8h2v5h2V8h2zM1 1h14v2H1z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/ExternalLink.svg b/editor/icons/ExternalLink.svg
index 506f36bd0a..a5042900b5 100644
--- a/editor/icons/ExternalLink.svg
+++ b/editor/icons/ExternalLink.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m4.939 8.939 5.5-5.5L7.999 1h7v7L12.56 5.561l-5.5 5.5zM12 15H4a3.079 3.079 0 0 1-3-3V4a3.04 3.04 0 0 1 3-3h2a1 1 0 0 1 0 2H4a1.04 1.04 0 0 0-1 1v8a1.083 1.083 0 0 0 1 1h8a1.068 1.068 0 0 0 1-1v-2a1 1 0 0 1 2 0v2a3.063 3.063 0 0 1-3 3z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 9 5.5-5.5L8 1h7v7l-2.5-2.5L7 11zm7 6H4a3 3 0 0 1-3-3V4a3 3 0 0 1 3-3h2a1 1 0 0 1 0 2H4a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Gizmo3DSamplePlayer.svg b/editor/icons/Gizmo3DSamplePlayer.svg
index dc379b6076..f6fe444e47 100644
--- a/editor/icons/Gizmo3DSamplePlayer.svg
+++ b/editor/icons/Gizmo3DSamplePlayer.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M63.766 8.01a8.006 8.006 0 0 0-5.42 2.336l-.002.002-29.656 29.658H16c-4.37 0-8 3.63-8 8v32c0 4.37 3.63 8 8 8h12.688l29.656 29.656c2.4 2.398 5.98 2.866 8.717 1.732 2.737-1.133 4.938-3.996 4.94-7.388V16.002c-.004-4.456-3.78-8.121-8.233-7.992zM112 12.004c-4.37 0-8 3.63-8 8v88c0 4.371 3.63 8 8 8s8-3.629 8-8v-88c0-4.37-3.63-8-8-8zm-24 24c-4.37 0-8 3.63-8 8v40c0 4.371 3.63 8 8 8s8-3.629 8-8v-40c0-4.37-3.63-8-8-8z" fill-opacity=".294"/><path d="M63.883 12.004a4 4 0 0 0-2.71 1.168l-30.829 30.83H16a4 4 0 0 0-4 4v32a4 4 0 0 0 4 4h14.344l30.828 30.828c2.52 2.518 6.827.734 6.828-2.828V16a4 4 0 0 0-4.117-3.996zM112 16a4 4 0 0 0-4 4v88a4 4 0 0 0 4 4 4 4 0 0 0 4-4V20a4 4 0 0 0-4-4zM88 40a4 4 0 0 0-4 4v40a4 4 0 0 0 4 4 4 4 0 0 0 4-4V44a4 4 0 0 0-4-4z" fill="#f7f5cf"/></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M63.766 8a8 8 0 0 0-5.42 2.336L28.682 40H16a8 8 0 0 0-8 8v32a8 8 0 0 0 8 8h12.682l29.664 29.664A8 8 0 0 0 72 112V16a8 8 0 0 0-8.234-8zM112 12a8 8 0 0 0-8 8v88a8 8 0 0 0 16 0V20a8 8 0 0 0-8-8zM88 36a8 8 0 0 0-8 8v40a8 8 0 0 0 16 0V44a8 8 0 0 0-8-8z" fill-opacity=".294"/><path d="M63.883 12.004a4 4 0 0 0-2.71 1.168l-30.829 30.83H16a4 4 0 0 0-4 4v32a4 4 0 0 0 4 4h14.344l30.828 30.828c2.52 2.518 6.827.734 6.828-2.828V16a4 4 0 0 0-4.117-3.996zM112 16a4 4 0 0 0-4 4v88a4 4 0 0 0 4 4 4 4 0 0 0 4-4V20a4 4 0 0 0-4-4zM88 40a4 4 0 0 0-4 4v40a4 4 0 0 0 4 4 4 4 0 0 0 4-4V44a4 4 0 0 0-4-4z" fill="#f7f5cf"/></svg>
diff --git a/editor/icons/GizmoAudioListener3D.svg b/editor/icons/GizmoAudioListener3D.svg
index 66a1f8fd5d..e84124f66b 100644
--- a/editor/icons/GizmoAudioListener3D.svg
+++ b/editor/icons/GizmoAudioListener3D.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M48 3.96c-24.252 0-44 19.746-44 43.998a4 4 0 0 0 4 4h16a4 4 0 0 0 4-4A19.94 19.94 0 0 1 48 27.96a19.94 19.94 0 0 1 20 19.998c0 13.174-3.206 16.05-7.68 19.78-2.06 1.716-4.628 3.22-7.596 5.364-1.446 1.044-3.33 2.468-5.098 4.7C45.662 80.282 44 83.782 44 87.958c0 4.78-.634 7.372-1.22 8.64-.588 1.266-.942 1.46-1.956 2.066-1.1.66-5.032 1.29-8.824 1.29l-.04.002H24a4 4 0 0 0-4 4v16a4 4 0 0 0 4 4h8.042c3.812 0 12.62.394 21.132-4.712 8.02-4.812 13.326-14.546 14.348-27.184 1.624-1.096 4.69-2.994 8.16-5.886C83.686 79.504 92 67.094 92 47.958 92 23.706 72.252 3.96 48 3.96zm63.614 8.004a4 4 0 0 0-2.188.534l-13.906 8.03A4 4 0 0 0 94.06 26a43.992 43.992 0 0 1 5.944 21.96 43.99 43.99 0 0 1-5.928 21.976 4 4 0 0 0 1.462 5.47l13.89 8.014a4 4 0 0 0 5.464-1.466 68.01 68.01 0 0 0 0-67.996 4 4 0 0 0-3.278-1.996z" fill-opacity=".294"/><path d="M48 7.96A40 39.998 0 0 0 8 47.958h16A24 24 0 0 1 48 23.96a24 24 0 0 1 24 23.998c0 14-4.33 18.86-9.12 22.852-2.396 1.996-5.038 3.53-7.814 5.536-1.388 1-2.866 2.126-4.304 3.94C49.322 82.102 48 84.958 48 87.958c0 10.22-2.54 12.59-5.118 14.138-2.578 1.546-6.882 1.86-10.882 1.86h-8v16h8c4 0 11.696.31 19.116-4.14 7.06-4.238 12.2-13.28 12.696-26 .184-.166.148-.156.62-.498 1.724-1.244 5.084-3.21 8.688-6.214C80.33 77.096 88 65.958 88 47.958A40 39.998 0 0 0 48 7.96zm63.426 8L97.52 23.992a48 47.998 0 0 1 6.484 23.966 48 47.998 0 0 1-6.468 23.984l13.89 8.014a64 63.996 0 0 0 0-63.996z" fill="#f7f5cf"/></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M48 4A44 44 0 0 0 4 48a4 4 0 0 0 4 4h16a4 4 0 0 0 4-4 20 20 0 0 1 40 0c0 13.174-3.206 16.05-7.68 19.78-2.06 1.716-4.628 3.22-7.596 5.364-1.446 1.044-3.33 2.468-5.098 4.7C45.662 80.282 44 83.782 44 87.958c0 4.78-.634 7.372-1.22 8.64-.588 1.266-.942 1.46-1.956 2.066-1.1.66-5.032 1.29-8.824 1.29l-.04.002H24a4 4 0 0 0-4 4v16a4 4 0 0 0 4 4h8.042c3.812 0 12.62.394 21.132-4.712 8.02-4.812 13.326-14.546 14.348-27.184 1.624-1.096 4.69-2.994 8.16-5.886C83.686 79.504 92 67.094 92 47.958a44 44 0 0 0-44-44zm63.614 8.004a4 4 0 0 0-2.188.534l-13.906 8.03A4 4 0 0 0 94.06 26a44 44 0 0 1 .016 43.936 4 4 0 0 0 1.462 5.47l13.89 8.014a4 4 0 0 0 5.464-1.466 68 68 0 0 0 0-68 4 4 0 0 0-3.278-2z" fill-opacity=".294"/><path d="M48 8A40 40 0 0 0 8 48h16a24 24 0 0 1 48 0c0 14-4.33 18.86-9.12 22.852-2.396 1.996-5.038 3.53-7.814 5.536-1.388 1-2.866 2.126-4.304 3.94-1.44 1.774-2.762 4.63-2.762 7.63 0 10.22-2.54 12.59-5.118 14.138-2.578 1.546-6.882 1.86-10.882 1.86h-8v16h8c4 0 11.696.31 19.116-4.14 7.06-4.238 12.2-13.28 12.696-26 .184-.166.148-.156.62-.498 1.724-1.244 5.084-3.21 8.688-6.214C80.33 77.096 88 65.958 88 47.958A40 40 0 0 0 48 8zm63.426 8L97.52 24a48 48 0 0 1 .016 47.942l13.89 8a64 64 0 0 0 0-64z" fill="#f7f5cf"/></svg>
diff --git a/editor/icons/GizmoCPUParticles3D.svg b/editor/icons/GizmoCPUParticles3D.svg
index 391c126c28..3dae7ade80 100644
--- a/editor/icons/GizmoCPUParticles3D.svg
+++ b/editor/icons/GizmoCPUParticles3D.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M35.504 1.282c-3.57 0-6.435 2.948-6.435 6.602v4.39c0 .889.17 1.726.478 2.499H19.465c-3.57 0-6.435 2.931-6.435 6.585v7.97c-.341-.057-.649-.203-1.006-.203h-4.29c-3.57 0-6.452 2.948-6.452 6.602v3.225c0 3.653 2.881 6.585 6.452 6.585h4.29c.358 0 .664-.146 1.006-.203v38.497c-.341-.057-.649-.203-1.006-.203h-4.29c-3.57 0-6.452 2.949-6.452 6.602v3.225c0 3.654 2.881 6.585 6.452 6.585h4.29c.358 0 .664-.145 1.006-.202v9.725c0 3.654 2.865 6.602 6.435 6.602h9.604v3.951c0 3.654 2.864 6.602 6.435 6.602h3.151c3.57 0 6.452-2.948 6.452-6.602v-3.95h37.225v3.95c0 3.654 2.865 6.602 6.435 6.602h3.152c3.57 0 6.451-2.948 6.451-6.602v-3.95h10.726c3.57 0 6.451-2.95 6.451-6.603v-9.607c.15.01.277.084.43.084h4.29c3.57 0 6.451-2.931 6.451-6.585V90.23c0-3.653-2.881-6.601-6.452-6.601h-4.29c-.152 0-.28.073-.429.084v-38.26c.15.01.277.084.43.084h4.29c3.57 0 6.451-2.932 6.451-6.585v-3.225c0-3.654-2.881-6.602-6.452-6.602h-4.29c-.152 0-.28.074-.429.084v-7.851c0-3.654-2.88-6.585-6.451-6.585h-11.22a6.7 6.7 0 0 0 .494-2.5v-4.39c0-3.653-2.88-6.601-6.451-6.601h-3.152c-3.57 0-6.435 2.948-6.435 6.602v4.39c0 .889.17 1.726.478 2.499H44.612c.31-.773.495-1.61.495-2.5v-4.39c0-3.653-2.881-6.601-6.452-6.601z" fill="#f7f5cf" stroke="#b3b3b3" stroke-width="2.564"/><path d="M62.861 21.662a27.707 31.503 0 0 1 27.144 25.411 18.472 18.902 0 0 1 15.956 18.691 18.472 18.902 0 0 1-18.48 18.894H38.225a18.472 18.902 0 0 1-18.464-18.894 18.472 18.902 0 0 1 15.923-18.708A27.707 31.503 0 0 1 62.86 21.662zM38.226 90.956a6.157 6.3 0 0 1 6.155 6.298 6.157 6.3 0 0 1-6.155 6.315 6.157 6.3 0 0 1-6.154-6.315 6.157 6.3 0 0 1 6.154-6.298zm49.254 0a6.157 6.3 0 0 1 6.171 6.298 6.157 6.3 0 0 1-6.17 6.315 6.157 6.3 0 0 1-6.156-6.315 6.157 6.3 0 0 1 6.155-6.298zm-24.619 6.298a6.157 6.3 0 0 1 6.155 6.315 6.157 6.3 0 0 1-6.155 6.298 6.157 6.3 0 0 1-6.154-6.298 6.157 6.3 0 0 1 6.154-6.315z" fill="#b3b3b3"/></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M36.688 4a6.112 6.112 0 0 0-6.112 6.112v4.8h-9.6a6.112 6.112 0 0 0-6.112 6.112v9.6h-4.8a6.112 6.112 0 0 0-6.112 6.112v3.2a6.112 6.112 0 0 0 6.112 6.112h4.8v36h-4.8a6.112 6.112 0 0 0-6.112 6.112v3.2a6.112 6.112 0 0 0 6.112 6.112h4.8v9.6a6.112 6.112 0 0 0 6.112 6.112h9.6v4.8a6.112 6.112 0 0 0 6.112 6.112h3.2A6.112 6.112 0 0 0 46 117.984v-4.8h36v4.8a6.112 6.112 0 0 0 6.112 6.112h3.2a6.112 6.112 0 0 0 6.112-6.112v-4.8h9.6a6.112 6.112 0 0 0 6.112-6.112v-9.6h4.8a6.112 6.112 0 0 0 6.112-6.112v-3.2a6.112 6.112 0 0 0-6.112-6.112h-4.8v-36h4.8a6.112 6.112 0 0 0 6.112-6.112v-3.2a6.112 6.112 0 0 0-6.112-6.112h-4.8v-9.6a6.112 6.112 0 0 0-6.112-6.104h-9.6v-4.8a6.112 6.112 0 0 0-6.112-6.112h-3.2A6.112 6.112 0 0 0 82 10.12v4.8H46v-4.8a6.112 6.112 0 0 0-6.112-6.112z" fill="#f7f5cf" stroke="#b3b3b3" stroke-width="3"/><path d="M88 82a18 18 0 0 0 2.484-35.814 27 30 0 0 0-52.944 0 18 18 0 0 0 2.484 35.802zm-48 6a6 6 0 0 0 0 12 6 6 0 0 0 0-12zm48 0a6 6 0 0 0 0 12 6 6 0 0 0 0-12zm-24 6a6 6 0 0 0 0 12 6 6 0 0 0 0-12z" fill="#b3b3b3"/></svg>
diff --git a/editor/icons/GizmoDirectionalLight.svg b/editor/icons/GizmoDirectionalLight.svg
index c736e86dee..c913e649f7 100644
--- a/editor/icons/GizmoDirectionalLight.svg
+++ b/editor/icons/GizmoDirectionalLight.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M64 4c-4.432 0-8 3.568-8 8v16c0 4.432 3.568 8 8 8s8-3.568 8-8V12c0-4.432-3.568-8-8-8zM27.23 19.223c-2.045 0-4.09.785-5.656 2.352a7.98 7.98 0 0 0 0 11.312L32.886 44.2c3.134 3.134 8.18 3.134 11.314 0s3.134-8.181 0-11.314L32.886 21.575a7.975 7.975 0 0 0-5.656-2.352zm73.539 0c-2.045 0-4.09.785-5.656 2.352L83.799 32.887c-3.134 3.134-3.134 8.18 0 11.314s8.18 3.134 11.314 0l11.312-11.314a7.98 7.98 0 0 0 0-11.312 7.975 7.975 0 0 0-5.656-2.352zM63.999 40a24 24 0 0 0-24 24 24 24 0 0 0 24 24 24 24 0 0 0 24-24 24 24 0 0 0-24-24zm-52 16c-4.432 0-8 3.568-8 8s3.568 8 8 8h16c4.432 0 8-3.568 8-8s-3.568-8-8-8zm88 0c-4.432 0-8 3.568-8 8s3.568 8 8 8h16c4.432 0 8-3.568 8-8s-3.568-8-8-8zM38.544 81.449a7.977 7.977 0 0 0-5.658 2.35L21.574 95.113a7.98 7.98 0 0 0 0 11.312 7.98 7.98 0 0 0 11.312 0L44.2 95.113a7.983 7.983 0 0 0 0-11.314 7.973 7.973 0 0 0-5.656-2.35zm50.91 0a7.97 7.97 0 0 0-5.656 2.35 7.983 7.983 0 0 0 0 11.314l11.314 11.312c3.134 3.133 8.178 3.133 11.312 0s3.134-8.179 0-11.312L95.112 83.799a7.977 7.977 0 0 0-5.658-2.35zM63.999 92c-4.432 0-8 3.568-8 8v16c0 4.432 3.568 8 8 8s8-3.568 8-8v-16c0-4.432-3.568-8-8-8z" fill-opacity=".294"/><path d="M64 8c-2.216 0-4 1.784-4 4v16c0 2.216 1.784 4 4 4s4-1.784 4-4V12c0-2.216-1.784-4-4-4zM27.23 23.227a3.987 3.987 0 0 0-2.828 1.176 3.99 3.99 0 0 0 0 5.656l11.312 11.314c1.567 1.567 4.091 1.567 5.658 0s1.567-4.091 0-5.658L30.058 24.403a3.987 3.987 0 0 0-2.828-1.176zm73.539 0a3.987 3.987 0 0 0-2.828 1.176L86.627 35.715c-1.567 1.567-1.567 4.091 0 5.658s4.091 1.567 5.658 0l11.313-11.314a3.99 3.99 0 0 0 0-5.656 3.987 3.987 0 0 0-2.828-1.176zM63.999 44c-11.046 0-20 8.954-20 20s8.954 20 20 20 20-8.954 20-20-8.954-20-20-20zm-52 16c-2.216 0-4 1.784-4 4s1.784 4 4 4h16c2.216 0 4-1.784 4-4s-1.784-4-4-4zm88 0c-2.216 0-4 1.784-4 4s1.784 4 4 4h16c2.216 0 4-1.784 4-4s-1.784-4-4-4zM38.544 85.453a3.99 3.99 0 0 0-2.83 1.174L24.402 97.94a3.99 3.99 0 0 0 0 5.656 3.99 3.99 0 0 0 5.656 0l11.314-11.313a3.993 3.993 0 0 0 0-5.658 3.985 3.985 0 0 0-2.828-1.174zm50.91 0c-1.022 0-2.045.39-2.828 1.174a3.993 3.993 0 0 0 0 5.658l11.314 11.313a3.99 3.99 0 0 0 5.656 0 3.99 3.99 0 0 0 0-5.656L92.283 86.628a3.99 3.99 0 0 0-2.83-1.174zM63.999 96c-2.216 0-4 1.784-4 4v16c0 2.216 1.784 4 4 4s4-1.784 4-4v-16c0-2.216-1.784-4-4-4z" fill="#fff"/></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M64 4a8 8 0 0 0-8 8v16a8 8 0 0 0 16 0V12a8 8 0 0 0-8-8zM27.23 19.223a8 8 0 0 0-5.656 13.664L32.886 44.2A8 8 0 0 0 44.2 32.886L32.886 21.575a7.975 7.975 0 0 0-5.656-2.352zm73.539 0a8 8 0 0 0-5.656 2.352L83.799 32.887a8 8 0 0 0 11.314 11.314l11.312-11.314a8 8 0 0 0-5.656-13.664zM64 40a24 24 0 0 0 0 48 24 24 0 0 0 0-48zM12 56a8 8 0 0 0 0 16h16a8 8 0 0 0 0-16zm88 0a8 8 0 0 0 0 16h16a8 8 0 0 0 0-16zM38.544 81.449a8 8 0 0 0-5.658 2.35L21.574 95.113a7.98 7.98 0 0 0 11.312 11.312L44.2 95.113a8 8 0 0 0-5.656-13.664zm50.91 0a8 8 0 0 0-5.656 13.664l11.314 11.312a8 8 0 0 0 11.312-11.312L95.112 83.799a8 8 0 0 0-5.658-2.35zM64 92a8 8 0 0 0-8 8v16a8 8 0 0 0 15.999 0v-16a8 8 0 0 0-8-8z" fill-opacity=".294"/><path d="M64 8a4 4 0 0 0-4 4v16a4 4 0 0 0 8 0V12a4 4 0 0 0-4-4zM27.23 23.227a4 4 0 0 0-2.828 6.832l11.312 11.314a4 4 0 0 0 5.658-5.658L30.058 24.403a4 4 0 0 0-2.828-1.176zm73.539 0a4 4 0 0 0-2.828 1.176L86.627 35.715a4 4 0 0 0 5.658 5.658l11.313-11.314a4 4 0 0 0-2.828-6.832zM64 44a20 20 0 0 0 0 40 20 20 0 0 0 0-40zM12 60a4 4 0 0 0 0 8h16a4 4 0 0 0 0-8zm88 0a1 1 0 0 0 0 8h16a1 1 0 0 0 0-8zM38.544 85.453a4 4 0 0 0-2.83 1.174L24.402 97.94a4 4 0 0 0 5.656 5.656l11.314-11.313a4 4 0 0 0-2.828-6.832zm50.91 0a4 4 0 0 0-2.828 6.832l11.314 11.313a4 4 0 0 0 5.656-5.656L92.283 86.628a4 4 0 0 0-2.83-1.174zM64 96a4 4 0 0 0-4 4v16a4 4 0 0 0 8 0v-16a4 4 0 0 0-4-4z" fill="#fff"/></svg>
diff --git a/editor/icons/GizmoSpotLight.svg b/editor/icons/GizmoSpotLight.svg
index c943ed7a7b..3c986aedd4 100644
--- a/editor/icons/GizmoSpotLight.svg
+++ b/editor/icons/GizmoSpotLight.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M52 4c-6.579 0-12 5.421-12 12v26.625c-12.263 7.282-19.978 19.75-20 33.369L19.994 80h28.578C50.372 86.863 56.6 92 64 92s13.628-5.137 15.428-12h28.576L108 75.996c-.015-13.625-7.732-26.099-20-33.385V16c0-6.579-5.42-12-12-12zM40.311 82.016a8.033 8.033 0 0 0-4.559 1.055l-10.393 6c-3.778 2.181-5.111 7.15-2.93 10.93 2.182 3.778 7.151 5.111 10.93 2.93l10.394-6c3.78-2.183 5.108-7.153 2.927-10.93a8.033 8.033 0 0 0-6.369-3.985zm47.379 0A8.032 8.032 0 0 0 81.32 86c-2.18 3.778-.851 8.748 2.929 10.93l10.393 6c3.779 2.182 8.748.85 10.93-2.93 2.182-3.779.849-8.747-2.93-10.93l-10.393-6a8.033 8.033 0 0 0-4.559-1.054zM64.001 96c-4.363 0-8 3.637-8 8v12c0 4.363 3.637 8 8 8s8-3.637 8-8v-12c0-4.363-3.637-8-8-8z" fill-opacity=".294"/><path d="M52 8c-4.432 0-8 3.568-8 8v28.875A40 36 0 0 0 24 76h28a12 12 0 0 0 12 12 12 12 0 0 0 12-12h28a40 36 0 0 0-20-31.141V16c0-4.432-3.568-8-8-8zM40.031 86.006a3.987 3.987 0 0 0-2.28.53l-10.392 6c-1.92 1.107-2.573 3.545-1.465 5.464s3.546 2.573 5.465 1.465l10.393-6A3.992 3.992 0 0 0 43.215 88a3.99 3.99 0 0 0-3.184-1.994zm47.938 0A3.99 3.99 0 0 0 84.785 88a3.992 3.992 0 0 0 1.463 5.465l10.393 6c1.92 1.108 4.357.454 5.465-1.465s.454-4.357-1.465-5.465l-10.393-6a3.987 3.987 0 0 0-2.279-.529zM64 100c-2.216 0-4 1.784-4 4v12c0 2.216 1.784 4 4 4s4-1.784 4-4v-12c0-2.216-1.784-4-4-4z" fill="#fff"/></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M52 4a12 12 0 0 0-12 12v26.625A38 38 0 0 0 20 76v4h28.578a16 16 0 0 0 30.856 0H108v-4a38 38 0 0 0-20-33.385V16A12 12 0 0 0 76 4zM40.311 82a8 8 0 0 0-4.559 1.055l-10.393 6a8 8 0 0 0 8 13.86l10.394-6A8 8 0 0 0 40.311 82zm47.379 0a8 8 0 0 0-3.441 14.93l10.393 6a8 8 0 0 0 8-13.86l-10.393-6a8 8 0 0 0-4.559-1.054zM64 96a8 8 0 0 0-8 8v12a8 8 0 0 0 16 0v-12a8 8 0 0 0-8-8z" fill-opacity=".294"/><path d="M52 8a8 8 0 0 0-8 8v28.875A40 36 0 0 0 24 76h28a12 12 0 0 0 24 0h28a40 36 0 0 0-20-31.141V16a8 8 0 0 0-8-8zM40.031 86a4 4 0 0 0-2.28.53l-10.392 6a4 4 0 0 0 4 6.929l10.393-6a4 4 0 0 0-1.721-7.453zm47.938 0a4 4 0 0 0-1.721 7.465l10.393 6a4 4 0 0 0 4-6.93l-10.393-6a4 4 0 0 0-2.279-.529zM64 100a4 4 0 0 0-4 4v12a4 4 0 0 0 8 0v-12a4 4 0 0 0-4-4z" fill="#fff"/></svg>
diff --git a/editor/icons/GizmoVoxelGI.svg b/editor/icons/GizmoVoxelGI.svg
index 92e84bc67e..b4aa912663 100644
--- a/editor/icons/GizmoVoxelGI.svg
+++ b/editor/icons/GizmoVoxelGI.svg
@@ -1 +1 @@
-<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M12 4a8 8 0 0 0-8 8v104a8 8 0 0 0 8 8h64v-16H20V20h88v7.768A36 36 0 0 0 92 24a36 36 0 0 0-36 36 36 36 0 0 0 16 29.9V98a9.977 9.977 0 0 0 8 9.8V124h24v-16.2a9.977 9.977 0 0 0 8-9.8v-8.088A36 36 0 0 0 128 60a36 36 0 0 0-19.523-32H124V12a8 8 0 0 0-8-8H12zm28.25 17.996A7.984 7.984 0 0 0 33.11 26a7.983 7.983 0 0 0 2.93 10.928l10.392 6c3.838 2.216 8.712.91 10.928-2.928s.91-8.712-2.928-10.928l-10.393-6a7.99 7.99 0 0 0-3.789-1.076zM92 44a16 16 0 0 1 16 16 16 16 0 0 1-16 16 16 16 0 0 1-16-16 16 16 0 0 1 16-16zm-60 8c-4.432 0-8 3.568-8 8s3.568 8 8 8h12c4.432 0 8-3.568 8-8s-3.568-8-8-8zm18.221 23.996a7.991 7.991 0 0 0-3.79 1.076l-10.392 6c-3.838 2.216-5.146 7.09-2.93 10.928s7.092 5.144 10.93 2.928l10.393-6A7.982 7.982 0 0 0 57.36 80a7.98 7.98 0 0 0-7.139-4.004z" fill-opacity=".294"/><path d="M12 8a4 4 0 0 0-4 4v104a4 4 0 0 0 4 4h60v-8H16V16h96v8h8V12a4 4 0 0 0-4-4zm27.715 17.951a3.843 3.843 0 0 0-3.066 1.92l-.149.258a3.844 3.844 0 0 0 1.41 5.261l10.648 6.149a3.844 3.844 0 0 0 5.262-1.41l.149-.258a3.844 3.844 0 0 0-1.41-5.262L41.91 26.461a3.84 3.84 0 0 0-2.195-.51zM92 28a32 32 0 0 0-32 32 32 32 0 0 0 16 27.668V96c0 4.432 3.568 8 8 8h16c4.432 0 8-3.568 8-8v-8.323A32 32 0 0 0 124 60a32 32 0 0 0-32-32zm0 12a20 20 0 0 1 20 20 20 20 0 0 1-20 20 20 20 0 0 1-20-20 20 20 0 0 1 20-20zM31.852 56A3.843 3.843 0 0 0 28 59.85v.297A3.843 3.843 0 0 0 31.852 64h12.297a3.843 3.843 0 0 0 3.852-3.852v-.297A3.843 3.843 0 0 0 44.149 56zm18.902 23.95a3.84 3.84 0 0 0-2.195.51L37.91 86.61a3.844 3.844 0 0 0-1.41 5.262l.148.257a3.844 3.844 0 0 0 5.262 1.41l10.648-6.148a3.844 3.844 0 0 0 1.41-5.261l-.149-.258a3.842 3.842 0 0 0-3.066-1.92zM84 112v8h16v-8z" fill="#f7f5cf"/></svg>
+<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg"><path d="M12 4a8 8 0 0 0-8 8v104a8 8 0 0 0 8 8h64v-16H20V20h88v7.768A36 36 0 0 0 72 89.9V98a9.977 9.977 0 0 0 8 9.8V124h24v-16.2a9.977 9.977 0 0 0 8-9.8v-8.088A36 36 0 0 0 108.477 28H124V12a8 8 0 0 0-8-8H12zm28.25 18a8 8 0 0 0-4.21 14.928l10.392 6a8 8 0 0 0 8-13.856l-10.393-6a8 8 0 0 0-3.789-1.076zM92 44a16 16 0 0 1 0 32 16 16 0 0 1 0-32zm-60 8a8 8 0 0 0 0 16h12a8 8 0 0 0 0-16zm18.221 24a8 8 0 0 0-3.79 1.076l-10.392 6a8 8 0 0 0 8 13.856l10.393-6a8 8 0 0 0-4.211-14.936z" fill-opacity=".294"/><path d="M12 8a4 4 0 0 0-4 4v104a4 4 0 0 0 4 4h60v-8H16V16h96v8h8V12a4 4 0 0 0-4-4zm27.715 17.951a3.843 3.843 0 0 0-3.066 1.92l-.149.258a3.844 3.844 0 0 0 1.41 5.261l10.648 6.149a3.844 3.844 0 0 0 5.262-1.41l.149-.258a3.844 3.844 0 0 0-1.41-5.262L41.91 26.461a3.84 3.84 0 0 0-2.195-.51zM92 28a32 32 0 0 0-32 32 32 32 0 0 0 16 27.668V96a8 8 0 0 0 8 8h16a8 8 0 0 0 8-8v-8.323A32 32 0 0 0 124 60a32 32 0 0 0-32-32zm0 12a20 20 0 0 1 0 40 20 20 0 0 1 0-40zM31.852 56a4 4 0 0 0 0 8h12.297a4 4 0 0 0 0-8zm18.902 23.95a4 4 0 0 0-2.195.51L37.91 86.61a4 4 0 0 0 3.852 6.672l10.648-6.148a4 4 0 0 0-1.656-7.181zM84 112v8h16v-8z" fill="#f7f5cf"/></svg>
diff --git a/editor/icons/GridToggle.svg b/editor/icons/GridToggle.svg
new file mode 100644
index 0000000000..595952c3af
--- /dev/null
+++ b/editor/icons/GridToggle.svg
@@ -0,0 +1 @@
+<svg viewBox="0 0 16 16" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 0v2H0v1h2v4H0v1h2v4H0v1h2v2h1v-2h3.125A5.5 5 0 016 12H3V8h4v1.143a5.5 5 0 011-.99V8h.223A5.5 5 0 0111.5 7H8V3h4v4h-.5a5.5 5 0 013.297 1H15V7h-2V3h2V2h-2V0h-1v2H8V0H7v2H3V0H2zm1 3h4v4H3V3zm4 11.857V15h.117A5.5 5 0 017 14.857z" fill="#e0e0e0"/><path d="M11.485 8.261c-1.648 0-3.734 1.256-4.485 3.68a.645.645 0 00-.004.367C7.721 14.846 9.873 16 11.486 16c1.612 0 3.764-1.154 4.489-3.692a.645.645 0 000-.356c-.71-2.443-2.842-3.691-4.49-3.691zm0 1.29a2.58 2.58 0 012.58 2.58 2.58 2.58 0 01-2.58 2.58 2.58 2.58 0 01-2.58-2.58 2.58 2.58 0 012.58-2.58zm0 1.29a1.29 1.29 0 00-1.29 1.29 1.29 1.29 0 001.29 1.29 1.29 1.29 0 001.29-1.29 1.29 1.29 0 00-1.29-1.29z" fill="#e0e0e0" /></svg>
diff --git a/editor/icons/GuiGraphNodePort.svg b/editor/icons/GuiGraphNodePort.svg
index d04dabcfc3..04645d97b7 100644
--- a/editor/icons/GuiGraphNodePort.svg
+++ b/editor/icons/GuiGraphNodePort.svg
@@ -1 +1 @@
-<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><circle cx="5" cy="5" fill="#fff" r="5"/></svg>
+<svg height="24" width="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" fill="#fff" r="10"/></svg>
diff --git a/editor/icons/GuiIndeterminate.svg b/editor/icons/GuiIndeterminate.svg
index 2174686f7a..ce4b204fdd 100644
--- a/editor/icons/GuiIndeterminate.svg
+++ b/editor/icons/GuiIndeterminate.svg
@@ -1 +1 @@
-<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M3.333 1A2.334 2.334 0 0 0 1 3.333v9.334A2.334 2.334 0 0 0 3.333 15h9.334A2.334 2.334 0 0 0 15 12.667V3.333A2.334 2.334 0 0 0 12.667 1z" fill="#699ce8"/><path d="M3 7h10v2H3z" fill="#fff"/></svg>
+<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" rx="2.33" height="14" width="14" fill="#699ce8"/><path d="M3 7h10v2H3z" fill="#fff"/></svg>
diff --git a/editor/icons/GuiSpinboxUpdown.svg b/editor/icons/GuiSpinboxUpdown.svg
index 5bfa6a1c09..70cfec1988 100644
--- a/editor/icons/GuiSpinboxUpdown.svg
+++ b/editor/icons/GuiSpinboxUpdown.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7.9844 1.002a1.0001 1.0001 0 0 0 -.69141.29102l-4 4a1.0001 1.0001 0 1 0 1.4141 1.4141l3.293-3.293 3.293 3.293a1.0001 1.0001 0 1 0 1.4141-1.4141l-4-4a1.0001 1.0001 0 0 0 -.72266-.29102zm4.0059 7.9844a1.0001 1.0001 0 0 0 -.69726.30664l-3.293 3.293-3.293-3.293a1.0001 1.0001 0 0 0 -.7168-.30273 1.0001 1.0001 0 0 0 -.69727 1.7168l4 4a1.0001 1.0001 0 0 0 1.4141 0l4-4a1.0001 1.0001 0 0 0 -.7168-1.7207z" fill="#e0e0e0" fill-opacity=".78431"/></svg>
+<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m4 6 4-4 4 4m0 4-4 4-4-4" fill="none" stroke="#e0e0e0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".784"/></svg>
diff --git a/editor/icons/GuiSpinboxUpdownDisabled.svg b/editor/icons/GuiSpinboxUpdownDisabled.svg
index 332c5e7bf8..6868a6f6ff 100644
--- a/editor/icons/GuiSpinboxUpdownDisabled.svg
+++ b/editor/icons/GuiSpinboxUpdownDisabled.svg
@@ -1 +1 @@
-<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7.984 1.002c-.259.004-.507.108-.691.291l-4 4c-.398.383-.41 1.016-.027 1.414s1.016.41 1.414.027l.027-.027 3.293-3.293 3.293 3.293c.383.398 1.016.41 1.414.027s.41-1.016.027-1.414c-.01-.009-.018-.018-.027-.027l-4-4c-.191-.19-.452-.296-.723-.291zm4.006 7.984c-.264.006-.514.117-.697.307l-3.293 3.293-3.293-3.293c-.188-.193-.447-.303-.717-.303-.552 0-1 .448-1 1 0 .271.109.529.303.717l4 4c.391.391 1.023.391 1.414 0l4-4c.398-.383.41-1.016.027-1.414-.193-.202-.463-.313-.744-.307z" fill="#b3b3b3" fill-opacity=".7843"/></svg>
+<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m4 6 4-4 4 4m0 4-4 4-4-4" fill="none" stroke="#b3b3b3" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".784"/></svg>
diff --git a/editor/icons/GuiTabMenu.svg b/editor/icons/GuiTabMenu.svg
index e031054f81..c4346ed8fb 100644
--- a/editor/icons/GuiTabMenu.svg
+++ b/editor/icons/GuiTabMenu.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m 8 0 A 2 2 0 0 0 8 4 A 2 2 0 0 0 8 0 z m 0 6 A 2 2 0 0 0 8 10 A 2 2 0 0 0 8 6 z m 0 6 A 2 2 0 0 0 8 16 A 2 2 0 0 0 8 12 z" fill="#e0e0e0" fill-opacity=".39216"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 0a2 2 0 0 0 0 4 2 2 0 0 0 0-4zm0 6a2 2 0 0 0 0 4 2 2 0 0 0 0-4zm0 6a2 2 0 0 0 0 4 2 2 0 0 0 0-4z" fill="#e0e0e0" fill-opacity=".39216"/></svg>
diff --git a/editor/icons/GuiTabMenuHl.svg b/editor/icons/GuiTabMenuHl.svg
index fa5b060a9b..c6d28dfa56 100644
--- a/editor/icons/GuiTabMenuHl.svg
+++ b/editor/icons/GuiTabMenuHl.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m 8 0 A 2 2 0 0 0 8 4 A 2 2 0 0 0 8 0 z m 0 6 A 2 2 0 0 0 8 10 A 2 2 0 0 0 8 6 z m 0 6 A 2 2 0 0 0 8 16 A 2 2 0 0 0 8 12 z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 0a2 2 0 0 0 0 4 2 2 0 0 0 0-4zm0 6a2 2 0 0 0 0 4 2 2 0 0 0 0-4zm0 6a2 2 0 0 0 0 4 2 2 0 0 0 0-4z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/GuiVisibilityHidden.svg b/editor/icons/GuiVisibilityHidden.svg
index ed164ea8c0..3a5c959b1d 100644
--- a/editor/icons/GuiVisibilityHidden.svg
+++ b/editor/icons/GuiVisibilityHidden.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2.9609 7.7266-1.9219.54883c.31999 1.12.8236 2.0593 1.4316 2.8398l-.83398.83398 1.4141 1.4141.84375-.84375c.98585.74762 2.0766 1.2067 3.1055 1.3867v1.0938h2v-1.0938c1.0288-.17998 2.1196-.6391 3.1055-1.3867l.84375.84375 1.4141-1.4141-.83398-.83398c.60804-.78055 1.1117-1.7199 1.4316-2.8398l-1.9219-.54883c-.8756 3.0646-3.5391 4.2734-5.0391 4.2734s-4.1635-1.2088-5.0391-4.2734z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2.96 7.727-1.921.548c.32 1.12.824 2.06 1.432 2.84l-.834.834 1.414 1.414.843-.843c.986.747 2.077 1.206 3.106 1.386V15h2v-1.094c1.029-.18 2.12-.639 3.105-1.386l.844.843 1.414-1.414-.834-.834a8.285 8.285 0 0 0 1.432-2.84l-1.922-.548C12.163 10.79 9.499 12 7.999 12s-4.163-1.209-5.038-4.273z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/HSlider.svg b/editor/icons/HSlider.svg
index c876afa843..a583dfe3f2 100644
--- a/editor/icons/HSlider.svg
+++ b/editor/icons/HSlider.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 3c-.55228 0-1 .44772-1 1v2c0 .55228.44772 1 1 1s1-.44772 1-1v-2c0-.55228-.44772-1-1-1zm12 0c-.55228 0-1 .44772-1 1v2c0 .55228.44772 1 1 1s1-.44772 1-1v-2c0-.55228-.44772-1-1-1zm-6 1c-.55228 0-1 .44772-1 1s.44772 1 1 1 1-.44772 1-1-.44772-1-1-1zm5 5c-1.1046 0-2 .89543-2 2 0 1.1046.89543 2 2 2 1.0099-.000337 1.8611-.75351 1.9844-1.7559.04003-.16104.03936-.32952-.002-.49024-.12404-1.0008-.97388-1.7527-1.9824-1.7539zm-11 1c-1.3523-.019125-1.3523 2.0191 0 2h7.1309c-.085635-.32648-.1296-.66248-.13086-1 .00189-.3376.046518-.67361.13281-1z" fill="#8eef97"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 11a2 2 0 0 0 4 0 2 2 0 0 0-4 0zm0-5a1 1 0 0 0 2 0V4a1 1 0 0 0-2 0zm5.867 4a4 4 0 0 1 0 2h7.13a1 1 0 0 0 0-2zM7 5a1 1 0 1 0 2 0 1 1 0 0 0-2 0zm6 1a1 1 0 0 0 2 0V4a1 1 0 0 0-2 0z" fill="#8eef97"/></svg>
diff --git a/editor/icons/Heart.svg b/editor/icons/Heart.svg
index 00edf90729..c4b21b6e43 100644
--- a/editor/icons/Heart.svg
+++ b/editor/icons/Heart.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 14c-.1249999 0-.25-.046875-.34375-.140625l-4.875-4.703125c-.0625-.054687-1.7812496-1.6249999-1.7812496-3.5 0-2.2890626 1.3984372-3.6562499 3.7343748-3.6562499 1.3671873 0 2.6484373 1.390625 3.2656248 1.9999999.6171875-.6093749 1.8984375-1.9999999 3.265625-1.9999999 2.335938 0 3.734375 1.3671873 3.734375 3.6562499 0 1.8750001-1.71875 3.4453125-1.789062 3.5156251l-4.867188 4.6874999c-.09375.09375-.2187499.140625-.34375.140625z" fill="#fc7f7f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 14a.485.485 0 0 1-.344-.14L2.781 9.155C2.72 9.102 1 7.531 1 5.656 1 3.367 2.398 2 4.734 2 6.102 2 7.383 3.39 8 4c.617-.61 1.898-2 3.266-2C13.602 2 15 3.367 15 5.656c0 1.875-1.719 3.446-1.79 3.516l-4.866 4.687A.485.485 0 0 1 8 14z" fill="#fc7f7f"/></svg>
diff --git a/editor/icons/Help.svg b/editor/icons/Help.svg
index b2a52e8ad9..ecb9d3e359 100644
--- a/editor/icons/Help.svg
+++ b/editor/icons/Help.svg
@@ -1 +1 @@
-<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M5.029 1C4.03.989 3.02 1.312 2 2v7c2.017-1.353 4.017-1.314 6 0 1.983-1.314 3.983-1.353 6 0V2c-1.02-.688-2.03-1.011-3.029-1-.662.007-1.318.173-1.971.463V6H8V2c-.982-.645-1.971-.989-2.971-1zM0 10v6h2a3 3 0 0 0 0-6zm5 3a3 3 0 0 0 6 0 3 3 0 0 0-6 0zm9 3h1v-2h-1a1 1 0 0 1 0-2h1v-2h-1a3 3 0 0 0 0 6zM2 12a1 1 0 0 1 0 2zm6 0a1 1 0 0 1 0 2 1 1 0 0 1 0-2z" fill="#e0e0e0" fill-opacity=".59"/></svg>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 2C6 .7 4 .7 2 2v7c2-1.3 4-1.3 6 0 2-1.3 4-1.3 6 0V2C12 .7 10.2.9 9 1.5V6H8zm-8 8v6h2a3 3 0 0 0 0-6zm5 3a3 3 0 0 0 6 0 3 3 0 0 0-6 0zm9 3h1v-2h-1a1 1 0 0 1 0-2h1v-2h-1a3 3 0 0 0 0 6zM2 12a1 1 0 0 1 0 2zm6 0a1 1 0 0 1 0 2 1 1 0 0 1 0-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/IOSDeviceWired.svg b/editor/icons/IOSDeviceWired.svg
new file mode 100644
index 0000000000..15bb0b4523
--- /dev/null
+++ b/editor/icons/IOSDeviceWired.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" viewBox="0 0 4.233 4.233"><path fill="#e0e0e0" d="M1.795.265a.53.53 0 0 0-.53.529v2.645c0 .293.237.53.53.53h1.587a.53.53 0 0 0 .53-.53V.794a.53.53 0 0 0-.53-.53zm0 .529h.256v.165a.247.247 0 0 0 .247.248h.58A.247.247 0 0 0 3.126.96V.794h.256v2.645H1.795z"/><path fill="#e0e0e0" d="M1.743 2.964a.178.178 0 0 0-.178-.178H1.18a.534.534 0 0 0-.503-.356v.178H.32v.178h.356v.356H.32v.178h.356v.178a.533.533 0 0 0 .502-.356h.387c.099 0 .178-.08.178-.178z" style="stroke-width:.177922"/></svg>
diff --git a/editor/icons/IOSDeviceWireless.svg b/editor/icons/IOSDeviceWireless.svg
new file mode 100644
index 0000000000..91fc679460
--- /dev/null
+++ b/editor/icons/IOSDeviceWireless.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" viewBox="0 0 4.233 4.233"><path fill="#e0e0e0" d="M1.795.265a.53.53 0 0 0-.53.529v2.645c0 .293.237.53.53.53h1.587a.53.53 0 0 0 .53-.53V.794a.53.53 0 0 0-.53-.53zm0 .529h.256v.165a.247.247 0 0 0 .247.248h.58A.247.247 0 0 0 3.126.96V.794h.256v2.645H1.795z"/><path d="M1.034 2.457a.444.444 0 0 1-.159-.34.444.444 0 0 1 .16-.34m-.286 1.02a.889.889 0 0 1-.318-.68.889.889 0 0 1 .318-.681" style="fill:none;fill-opacity:.996078;stroke:#e0e0e0;stroke-width:.222194;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:16.5;stroke-opacity:1;paint-order:stroke markers fill"/></svg>
diff --git a/editor/icons/IOSSimulator.svg b/editor/icons/IOSSimulator.svg
new file mode 100644
index 0000000000..59ab11a8a5
--- /dev/null
+++ b/editor/icons/IOSSimulator.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="16" viewBox="0 0 4.233 4.233" width="16"><path d="M1.399.735H.32v2.763h1.078c0-.004-.006-.006-.006-.01V.745c0-.004.006-.006.006-.01z" fill="#4bb7f8"/><path d="M.49.9v2.432h.62v-.18H.67v-.35h.44v-.18H.67V1.08h.44V.9H.49z" fill="#e0e0e0"/><path d="M1.795.265a.53.53 0 0 0-.53.529v2.645c0 .293.237.53.53.53h1.587a.53.53 0 0 0 .53-.53V.794a.53.53 0 0 0-.53-.53zm0 .529h.256v.165a.247.247 0 0 0 .247.248h.58A.247.247 0 0 0 3.126.96V.794h.256v2.645H1.795z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Info.svg b/editor/icons/Info.svg
new file mode 100644
index 0000000000..1a16f74069
--- /dev/null
+++ b/editor/icons/Info.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M8 1a7 7 0 0 0 0 14A7 7 0 0 0 8 1m1 12H7v-2h2v2M4.5 6a3.5 3.4 0 1 1 7 0C11.3 8.9 9 8.5 9 10H7c.1-2.6 2.4-2.8 2.5-4a1.5 1.4 0 0 0-3 0z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/KeyInvalid.svg b/editor/icons/KeyInvalid.svg
index fce600e6f0..519f25513f 100644
--- a/editor/icons/KeyInvalid.svg
+++ b/editor/icons/KeyInvalid.svg
@@ -1 +1 @@
-<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><path d="M.464 1.879 2.586 4 .464 6.122 1.88 7.536 4 5.414l2.121 2.122 1.414-1.414L5.415 4l2.119-2.121L6.121.465 4 2.586 1.879.465.464 1.879z" fill="#ff5f5f"/></svg>
+<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><path d="M.464 1.879 2.585 4 .464 6.121l1.414 1.414 2.121-2.121L6.12 7.535l1.414-1.414L5.413 4l2.121-2.121L6.12.465 3.999 2.586 1.878.465z" fill="#ff5f5f"/></svg>
diff --git a/editor/icons/KeyPosition.svg b/editor/icons/KeyPosition.svg
index c0fa703462..37ba41f996 100644
--- a/editor/icons/KeyPosition.svg
+++ b/editor/icons/KeyPosition.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-.195 0-.38964.07519-.53906.22461l-3.2363 3.2363c-.29884.29884-.29884.77929 0 1.0781l3.2363 3.2363c.29884.29884.77929.29884 1.0781 0l3.2363-3.2363c.29884-.29884.29884-.77929 0-1.0781l-3.2363-3.2363c-.14942-.14942-.34406-.22461-.53906-.22461zm-7 7v5c0 1.6569 1.3471 3.114 3 3h1v-2h-1c-.55228-.00001-.99999-.44772-1-1v-5zm7 2c-1.645 0-3 1.355-3 3s1.355 3 3 3 3-1.355 3-3-1.355-3-3-3zm3 3c0 1.6569 1.3431 3 3 3h1v-2h-1c-.55228-.00001-.99999-.44772-1-1 .00001-.55228.44772-.99999 1-1h1v-2h-1c-1.6569 0-3 1.3431-3 3zm-3-1c.56413 0 1 .4359 1 1 0 .5642-.43587 1-1 1s-1-.4358-1-1c0-.5641.43587-1 1-1z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1a.76.76 0 0 0-.54.225L4.226 4.46a.76.76 0 0 0 0 1.078L7.46 8.775a.76.76 0 0 0 1.078 0l3.236-3.236a.76.76 0 0 0 0-1.078L8.54 1.225A.76.76 0 0 0 8 1zM1 8v5a3 3 0 0 0 3 3h1v-2H4a1 1 0 0 1-1-1V8zm7 2a3 3 0 0 0 0 6 3 3 0 0 0 0-6zm6 0a3 3 0 0 0 0 6h1v-2h-1a1 1 0 0 1 0-2h1v-2h-1zm-6 2a1 1 0 0 1 0 2 1 1 0 0 1 0-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/KeyScale.svg b/editor/icons/KeyScale.svg
index 5caf80e68e..c7b55028ab 100644
--- a/editor/icons/KeyScale.svg
+++ b/editor/icons/KeyScale.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-.195 0-.38964.07519-.53906.22461l-3.2363 3.2363c-.29884.29884-.29884.77929 0 1.0781l3.2363 3.2363c.29884.29884.77929.29884 1.0781 0l3.2363-3.2363c.29884-.29884.29884-.77929 0-1.0781l-3.2363-3.2363c-.14942-.14942-.34406-.22461-.53906-.22461zm3 7v5c0 1.6569 1.3431 3 3 3h1v-2h-1c-.55228-.00001-.99999-.44772-1-1v-5zm-8 2c-.71466-.0001-1.3751.3811-1.7324 1-.35727.6188-.35727 1.3812 0 2 .35733.6189 1.0178 1.0001 1.7324 1h-2v2h2c.71466.0001 1.3751-.3811 1.7324-1 .35727-.6188.35727-1.3812 0-2-.35733-.6189-1.0178-1.0001-1.7324-1h2v-2zm6 0c-1.6569 0-3 1.3431-3 3s1.3431 3 3 3h1v-2h-1c-.55228-.00001-.99999-.44772-1-1 .0000096-.55228.44772-.99999 1-1h1v-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1a.76.76 0 0 0-.54.225L4.226 4.46a.76.76 0 0 0 0 1.078L7.46 8.775a.76.76 0 0 0 1.078 0l3.236-3.236a.76.76 0 0 0 0-1.078L8.54 1.225A.76.76 0 0 0 8 1zm3 7v5a3 3 0 0 0 3 3h1v-2h-1a1 1 0 0 1-1-1V8zm-8 2a2 2 0 1 0 0 4H1v2h2a2 2 0 1 0 0-4h2v-2zm6 0a3 3 0 1 0 0 6h1v-2H9a1 1 0 0 1 0-2h1v-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Load.svg b/editor/icons/Load.svg
index a049454ebb..a4a6b30d2d 100644
--- a/editor/icons/Load.svg
+++ b/editor/icons/Load.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 2c-1.1046 0-2 .8954-2 2v9c0 1.1046.89543 2 2 2h9c1.1046 0 1.8184-.91043 2-2l1-6c.003977-.18354-.042648-.3412-.13477-.5-.17849-.30916-.50825-.49972-.86523-.5h-8c-.35698.0002824-.68674.19084-.86523.5-.092118.1588-.13874.3399-.13477.52344l-1 5.9766c-.091144.54473-.44772 1-1 1s-1-.4477-1-1v-9c0-.5523.44772-1 1-1h2c.55228 0 1 .4477 1 1a1 1 0 0 0 .29297.70703 1 1 0 0 0 .70703.29297h4 1a1 1 0 0 0 -.29297-.70703 1 1 0 0 0 -.70703-.29297h-4c0-1.1046-.89543-2-2-2h-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 2a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9c1.105 0 1.818-.91 2-2l1-6a1 1 0 0 0-1-1H6c-.552 0-.909.455-1 1l-1 6c-.091.545-.448 1-1 1a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1 1 1 0 0 0 1 1h5a1 1 0 0 0-1-1H7a2 2 0 0 0-2-2H3z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/LockViewport.svg b/editor/icons/LockViewport.svg
index e1d26bc4b0..c29d1415a3 100644
--- a/editor/icons/LockViewport.svg
+++ b/editor/icons/LockViewport.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 0a6 6 0 0 0 -6 6v1h-1v9h14v-9h-1v-1a6 6 0 0 0 -6-6zm0 4c1.1046 0 2 .89543 2 2v1h-4v-1c0-1.1046.89543-2 2-2z" fill-opacity=".39216" stroke-linecap="round" stroke-linejoin="round" stroke-width="4"/><path d="m8 1a5 5 0 0 0 -5 5v2h-1v7h12v-7h-1v-2a5 5 0 0 0 -5-5zm0 2a3 3 0 0 1 3 3v2h-6v-2a3 3 0 0 1 3-3zm-1 7h2v3h-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 6v1H1v9h14V7h-1V6A6 6 0 0 0 2 6zm8 0v1H6V6a1 1 0 0 1 4 0z" fill-opacity=".392"/><path d="M3 6v2H2v7h12V8h-1V6A5 5 0 0 0 3 6zm8 0v2H5V6a3 3 0 0 1 6 0zm-4 4h2v3H7z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Loop.svg b/editor/icons/Loop.svg
index 9bbf168189..cef695602f 100644
--- a/editor/icons/Loop.svg
+++ b/editor/icons/Loop.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1v2h-2a5 5 0 0 0 -5 5 5 5 0 0 0 1.0039 2.9961l1.4355-1.4355a3 3 0 0 1 -.43945-1.5605 3 3 0 0 1 3-3h2v2l2-1.5 2-1.5-2-1.5-2-1.5zm5.9961 4.0039-1.4355 1.4355a3 3 0 0 1 .43945 1.5605 3 3 0 0 1 -3 3h-2v-2l-2 1.5-2 1.5 2 1.5 2 1.5v-2h2a5 5 0 0 0 5-5 5 5 0 0 0 -1.0039-2.9961z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1v2H6a5 5 0 0 0-4 8l1.414-1.414A3 3 0 0 1 6 5h2v2l4-3-4-3zm6 4-1.414 1.414A3 3 0 0 1 10 11H8V9l-4 3 4 3v-2h2a5 5 0 0 0 4-8z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/MatchCase.svg b/editor/icons/MatchCase.svg
index 0787b0aa56..62e8bc9218 100644
--- a/editor/icons/MatchCase.svg
+++ b/editor/icons/MatchCase.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m4 1c-2.2091-.00000066-4.069 1.7919-4 4v10h2v-4h4v4h2v-10c0-2.2091-1.7909-4-4-4zm5 11c0 1.6569 1.3431 3 3 3 .3409-.0014.67908-.0608 1-.17578v.17578h2v-6c0-1.6569-1.3431-3-3-3h-1v2h1c.55228 0 1 .44772 1 1v.17383c-.32104-.11432-.65921-.1731-1-.17383-1.6569 0-3 1.3431-3 3zm-5-9c1.1046-.0000001 1.914.89879 2 2v4h-4v-4c0-1.1046.89543-2 2-2zm8 8c.55228 0 1 .44772 1 1s-.44772 1-1 1-1-.44772-1-1 .44772-1 1-1z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4 1a4 4 0 0 0-4 4v10h2v-4h4v4h2V5a4 4 0 0 0-4-4zm5 11a3 3 0 0 0 4 2.824V15h2V9a3 3 0 0 0-3-3h-1v2h1a1 1 0 0 1 1 1v.174A3 3 0 0 0 9 12zM4 3a2 2 0 0 1 2 2v4H2V5a2 2 0 0 1 2-2zm8 8a1 1 0 1 1 0 2 1 1 0 0 1 0-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/MeshLibrary.svg b/editor/icons/MeshLibrary.svg
index 5d64acd97e..62a2b3efcc 100644
--- a/editor/icons/MeshLibrary.svg
+++ b/editor/icons/MeshLibrary.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1a2 2 0 0 0 -2 2 2 2 0 0 0 1 1.7305v6.541a2 2 0 0 0 -1 1.7285 2 2 0 0 0 2 2 2 2 0 0 0 1.7305-1h2.2695v-2h-2.2715a2 2 0 0 0 -.72852-.73047v-5.8555l3 3v-.41406a2.0002 2.0002 0 0 1 .80859-1.6055l-2.3945-2.3945h5.8574a2 2 0 0 0 .72852.73047v1.2695a2.0002 2.0002 0 0 1 .99805.27148 2.0002 2.0002 0 0 1 1.002-.27148v-1.2715a2 2 0 0 0 1-1.7285 2 2 0 0 0 -2-2 2 2 0 0 0 -1.7305 1h-6.541a2 2 0 0 0 -1.7285-1zm6 7v1 5 1h5c.55228 0 1-.4477 1-1v-5c0-.5523-.44772-1-1-1v4l-1-1-1 1v-4z" fill="#ffca5f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.729 2A2 2 0 1 0 2 4.73v6.542A2 2 0 1 0 4.73 14H7v-2H4.728A2 2 0 0 0 4 11.27V5.414l3 3V8a2 2 0 0 1 .809-1.606L5.414 4h5.858a2 2 0 0 0 .728.73V6a2 2 0 0 1 1 .271A2 2 0 0 1 14 6V4.728A2 2 0 1 0 11.27 2zM9 8v7h5a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v4l-1-1-1 1V8z" fill="#ffca5f"/></svg>
diff --git a/editor/icons/MeshTexture.svg b/editor/icons/MeshTexture.svg
index f85bc433e3..a3337cf8d5 100644
--- a/editor/icons/MeshTexture.svg
+++ b/editor/icons/MeshTexture.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.729 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.73 14h6.541a2 2 0 1 0 2.698-2.75H14V4.729A2 2 0 1 0 11.27 2H4.729zm6.542 2a2 2 0 0 0 .729.729v6.542a2 2 0 0 0-.729.729H4.729A2 2 0 0 0 4 11.271V4.729A2 2 0 0 0 4.729 4zM8.812 6.25v.701H8v.7H6.375v.699h-.813v.699H4.75v.701h6.5v-1.4h-.813v-1.4h-.812v-.7h-.813z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.729 2A2 2 0 1 0 2 4.73v6.541A2 2 0 1 0 4.73 14h6.541a2 2 0 1 0 2.698-2.75H14V4.729A2 2 0 1 0 11.27 2H4.729zm6.542 2a2 2 0 0 0 .729.729v6.542a2 2 0 0 0-.729.729H4.729A2 2 0 0 0 4 11.271V4.729A2 2 0 0 0 4.729 4zM8.812 6.25v.701H8v.7H6.375v.699h-.813v.699H4.75v.701h6.5v-1.4h-.813v-1.4h-.812v-.7z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/MethodOverrideAndSlot.svg b/editor/icons/MethodOverrideAndSlot.svg
index d3bd9f0253..eb9729b04d 100644
--- a/editor/icons/MethodOverrideAndSlot.svg
+++ b/editor/icons/MethodOverrideAndSlot.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 4.2333332 4.2333332" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m.15761184 3.636193h.37155483l.004252-.7093212c.0027092-.6681099.12999225-1.1321001.92674393-1.1328214h.1273374l.0042585-.7357171 1.3186582 1.006832-1.3229167 1.0700676v-.8531081h-.1260545c-.2888876 0-.3972562.2847204-.4031411.6391525 0 .2833012.0000193.4455045.0000289.7134508h1.2412654v.4907171h-2.14198686z" fill="#5fb2ff"/><path d="m2.38125.79375h1.5875v2.6458333h-1.5875v-.5291666h1.0583333v-1.5875h-1.0583333z" fill="#5fff97"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M.5 13.5H2V11c0-2.5.5-4.2 3.5-4.2H6V4l5 3.8-5 3.8V9h-.5C4.25 9 4 10 4 11v2.5h4.5v2h-8z" fill="#5fb2ff"/><path d="M9 3h6v10H9v-2h4V5H9z" fill="#5fff97"/></svg>
diff --git a/editor/icons/NodeInfo.svg b/editor/icons/NodeInfo.svg
index 4e3f0c42d0..732bccf0d0 100644
--- a/editor/icons/NodeInfo.svg
+++ b/editor/icons/NodeInfo.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0 -7-7zm-1 3h2v2h-2zm0 3h2v5h-2z" fill="#fff"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1a7 7 0 0 0 0 14A7 7 0 0 0 8 1zM7 4h2v2H7zm0 3h2v5H7z" fill="#fff"/></svg>
diff --git a/editor/icons/ORMMaterial3D.svg b/editor/icons/ORMMaterial3D.svg
index e09208155d..a70c44fe7d 100644
--- a/editor/icons/ORMMaterial3D.svg
+++ b/editor/icons/ORMMaterial3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 3.1191406-3.7636719 1.8808594 3.7636719 1.8828125 3.763672-1.8828125z" fill="#80ff45"/><path d="m3 6.6191406v2.3808594 1.382812l1.234375.617188 2.765625 1.382812v-1.382812-2-.3808594l-3.2382812-1.6191406z" fill="#ff4545"/><path d="m13 6.6191406-.761719.3808594-3.238281 1.6191406v3.7636714l2.765625-1.382812 1.234375-.617188v-1.382812z" fill="#45d7ff"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M7.75 3.375 4 5.25l3.75 1.875L11.5 5.25z" fill="#80ff45"/><path d="M3.375 6.5v3.75L7 12.063v-3.75z" fill="#ff4545"/><path d="M12.125 6.5 8.5 8.313v3.75l3.625-1.813z" fill="#45d7ff"/></svg>
diff --git a/editor/icons/Play.svg b/editor/icons/Play.svg
index a220cbb3d7..385d5013b1 100644
--- a/editor/icons/Play.svg
+++ b/editor/icons/Play.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.988 3c-.547.01-.987.451-.988.998v8a1 1 0 0 0 1.555.832l6-4a1 1 0 0 0 0-1.664l-6-4A1 1 0 0 0 4.988 3z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4 12a1 1 0 0 0 1.555.832l6-4a1 1 0 0 0 0-1.664l-6-4A1 1 0 0 0 4 4z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Popup.svg b/editor/icons/Popup.svg
index c25cc5b256..a6cfe0715f 100644
--- a/editor/icons/Popup.svg
+++ b/editor/icons/Popup.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.1046 0-2 .89543-2 2v10c0 1.1046.89543 2 2 2h10c1.1046 0 2-.89543 2-2v-10c0-1.1046-.89543-2-2-2zm4 2h2v6h-2zm0 8h2v2h-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2zm4 2h2v6H7zm0 8h2v2H7z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/ProgressBar.svg b/editor/icons/ProgressBar.svg
index 5c49563f23..894def26d8 100644
--- a/editor/icons/ProgressBar.svg
+++ b/editor/icons/ProgressBar.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 3c-1.1046 0-2 .89543-2 2v6c0 1.1046.89543 2 2 2h10c1.1046 0 2-.89543 2-2v-6c0-1.1046-.89543-2-2-2zm0 2h10v6h-10zm1 1v4h1v-4zm2 0v4h1v-4zm2 0v4h1v-4z" fill="#8eef97"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm0 2h10v6H3zm1 1v4h1V6zm2 0v4h1V6zm2 0v4h1V6z" fill="#8eef97"/></svg>
diff --git a/editor/icons/ProxyTexture.svg b/editor/icons/ProxyTexture.svg
index 5435e72a1b..5fe39f4da8 100644
--- a/editor/icons/ProxyTexture.svg
+++ b/editor/icons/ProxyTexture.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v4h4v-4zm6 0v2h6v8h-6v4h7a1 1 0 0 0 1-1v-12a1 1 0 0 0 -1-1zm2 4v1h-1v1h-1v3h1 2 2v-2h-1v-2h-1v-1zm-8 1v4h4v-4zm0 5v4h4v-4z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 1v4h4V1zm6 0v2h6v8H7v4h7a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm2 4v1H8v1H7v3h5V8h-1V6h-1V5zM1 6v4h4V6zm0 5v4h4v-4z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Rename.svg b/editor/icons/Rename.svg
index 853f68b2e1..bd6dad207b 100644
--- a/editor/icons/Rename.svg
+++ b/editor/icons/Rename.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 2v2h2v8h-2v2h2c.55228 0 1-.4477 1-1 0 .5523.44772 1 1 1h2v-2h-2v-8h2v-2h-2c-.55228 0-1 .44772-1 1 0-.55228-.44772-1-1-1z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M5 2v2h2v8H5v2h2a1 1 0 0 0 1-1 1 1 0 0 0 1 1h2v-2H9V4h2V2H9a1 1 0 0 0-1 1 1 1 0 0 0-1-1z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/ShaderGlobalsOverride.svg b/editor/icons/ShaderGlobalsOverride.svg
index 3a4e4cfb2c..8ac50f05b2 100644
--- a/editor/icons/ShaderGlobalsOverride.svg
+++ b/editor/icons/ShaderGlobalsOverride.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1c-.55226.0001-.99994.4477-1 1v12c.0000552.5523.44774.9999 1 1h12c.55226-.0001.99994-.4477 1-1v-8l-5-5zm1 2h6v3c0 .554.44599 1 1 1h3v6h-10zm1 1v1h3v-1zm0 2v1h2v-1zm3 0v1h1v-1zm-2 3 3 3 3-3z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V6l-5-5zm1 2h6v3a1 1 0 0 0 1 1h3v6H3zm1 1v1h3V4zm0 2v1h2V6zm3 0v1h1V6zM5 9l3 3 3-3z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Sort.svg b/editor/icons/Sort.svg
index a7f01fd24e..da18d50010 100644
--- a/editor/icons/Sort.svg
+++ b/editor/icons/Sort.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m9 1v2h6v-2zm-5.0156.0019531a1.0001 1.0001 0 0 0 -.69141.29102l-2 2a1 1 0 0 0 0 1.4141 1 1 0 0 0 1.4141 0l.29297-.29297v7.1719l-.29297-.29297a1 1 0 0 0 -.7207-.29102 1 1 0 0 0 -.69336.29102 1 1 0 0 0 0 1.4141l2 2a1.0001 1.0001 0 0 0 1.4141 0l2-2a1 1 0 0 0 0-1.4141 1 1 0 0 0 -1.4141 0l-.29297.29297v-7.1719l.29297.29297a1 1 0 0 0 1.4141 0 1 1 0 0 0 0-1.4141l-2-2a1.0001 1.0001 0 0 0 -.72266-.29102zm5.0156 5.998v2h4v-2zm0 6v2h2v-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M9 1v2h6V1zM4 1a1 1 0 0 0-.691.291l-2 2a1 1 0 0 0 1.414 1.414l.293-.293v7.172l-.293-.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l2-2a1 1 0 0 0-1.414-1.414l-.293.293V4.412l.293.293a1 1 0 0 0 1.414-1.414l-2-2A1 1 0 0 0 4 1zm5 6v2h4V7zm0 6v2h2v-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/SpotLight3D.svg b/editor/icons/SpotLight3D.svg
index 27c318257a..0d43545d04 100644
--- a/editor/icons/SpotLight3D.svg
+++ b/editor/icons/SpotLight3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1a1 1 0 0 0 -1 1v3.6934c-1.7861.86608-3 2.4605-3 4.3066h4a2 2 0 0 0 2 2 2 2 0 0 0 2-2h4c0-1.8462-1.2139-3.4406-3-4.3066v-3.6934a1 1 0 0 0 -1-1zm-1.0977 9.6348-1.7324 1 1 1.7305 1.7324-1zm6.1953 0-1 1.7305 1.7324 1 1-1.7305zm-4.0977 2.3652v2h2v-2z" fill="#fc7f7f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M6 1a1 1 0 0 0-1 1v3.693A5 5 0 0 0 2 10h4a2 2 0 0 0 4 0h4a5 5 0 0 0-3-4.307V2a1 1 0 0 0-1-1zm-1.098 9.635-1.732 1 1 1.73 1.732-1zm6.196 0-1 1.73 1.732 1 1-1.73zM7 13v2h2v-2z" fill="#fc7f7f"/></svg>
diff --git a/editor/icons/SpriteFrames.svg b/editor/icons/SpriteFrames.svg
index 8ab0ec2c00..a6aecc46bc 100644
--- a/editor/icons/SpriteFrames.svg
+++ b/editor/icons/SpriteFrames.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.108 0-2 .89199-2 2v6c0 1.108.89199 2 2 2h6c1.108 0 2-.89199 2-2v-6c0-1.108-.89199-2-2-2zm10 0v2h2v-2zm-10 4c.554 0 1 .446 1 1v1c0 .554-.446 1-1 1s-1-.446-1-1v-1c0-.554.446-1 1-1zm6 0c.554 0 1 .446 1 1v1c0 .554-.446 1-1 1s-1-.446-1-1v-1c0-.554.446-1 1-1zm4 0v2h2v-2zm-9 4h2 2a2 1 0 0 1 -1 .86523 2 1 0 0 1 -2 0 2 1 0 0 1 -1-.86523zm9 0v2h2v-2zm-12 4v2h2v-2zm4 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2v-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 1a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2zm10 0v2h2V1zM2 6a1 1 0 0 1 2 0v1a1 1 0 0 1-2 0zm6 0a1 1 0 0 1 2 0v1a1 1 0 0 1-2 0zm5-1v2h2V5zM4 9h4a2 1 0 0 1-4 0zm9 0v2h2V9zM1 13v2h2v-2zm4 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2v-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/SpriteSheet.svg b/editor/icons/SpriteSheet.svg
index a162037f99..2e2c512d38 100644
--- a/editor/icons/SpriteSheet.svg
+++ b/editor/icons/SpriteSheet.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.1046 0-2 .89543-2 2v10c0 1.1046.89543 2 2 2h10c1.1046 0 2-.89543 2-2v-10c0-1.1046-.89543-2-2-2zm0 2h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm-8 4h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm-8 4h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2zm0 2h2v2H3zm4 0h2v2H7zm4 0h2v2h-2zM3 7h2v2H3zm4 0h2v2H7zm4 0h2v2h-2zm-8 4h2v2H3zm4 0h2v2H7zm4 0h2v2h-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/TextureButton.svg b/editor/icons/TextureButton.svg
index 8d3d1c52ce..39ec6722f9 100644
--- a/editor/icons/TextureButton.svg
+++ b/editor/icons/TextureButton.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1v2h6v10h-4v2h6v-14zm-5 1v3.1328l-1.4453-.96484-1.1094 1.6641 3 2c.3359.2239.77347.2239 1.1094 0l3-2-1.1094-1.6641-1.4453.96484v-3.1328zm7 4v1h-1v1h-1v1h1v2h2 2v-2h-1v-2h-1v-1zm-7.5 4c-.831 0-1.5.669-1.5 1.5v.5 1h-1v2h8v-2h-1v-1-.5c0-.831-.669-1.5-1.5-1.5z" fill="#8eef97"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1v2h6v10h-4v2h6V1zM3 2v3.133l-1.445-.965-1.11 1.664 3 2a1 1 0 0 0 1.11 0l3-2-1.11-1.664L5 5.133V2zm7 4v1H9v1H8v1h1v2h4V9h-1V7h-1V6zm-7.5 4A1.5 1.5 0 0 0 1 11.5V13H0v2h8v-2H7v-1.5A1.5 1.5 0 0 0 5.5 10z" fill="#8eef97"/></svg>
diff --git a/editor/icons/ToolPan.svg b/editor/icons/ToolPan.svg
index 36ad59a449..a51e3e76a7 100644
--- a/editor/icons/ToolPan.svg
+++ b/editor/icons/ToolPan.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M11 2a1 1 0 0 0-2 0v6H8V3a1 1 0 0 0-2 0v8.05l-2.5-1.8A1 1 0 0 0 1.875 11L6 15h6a2 2 0 0 0 2-2V4a1 1 0 0 0-2 0v4h-1V2z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M11 2a1 1 0 0 0-2 0v6H8V3a1 1 0 0 0-2 0v8.05l-2.5-1.8A1 1 0 0 0 1.875 11L6 15h6a2 2 0 0 0 2-2V4a1 1 0 0 0-2 0v4h-1z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Tools.svg b/editor/icons/Tools.svg
index 81e7385945..e1183db0b1 100644
--- a/editor/icons/Tools.svg
+++ b/editor/icons/Tools.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m4 1-1 2 1 2v4h-2v3 .5c0 1.385 1.115 2.5 2.5 2.5s2.5-1.115 2.5-2.5v-1-2.5h-2v-4l1-2-1-2zm6 .17383a3 3 0 0 0 -2 2.8262 3 3 0 0 0 2 2.8262v6.1738 1c0 .554.446 1 1 1s1-.446 1-1v-4-3.1758a3 3 0 0 0 2-2.8242 3 3 0 0 0 -2-2.8242v2.8242a1 1 0 0 1 -1 1 1 1 0 0 1 -1-1v-2.8262z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4 1 3 3l1 2v4H2v3.5a2.5 2.5 0 0 0 5 0V9H5V5l1-2-1-2zm6 .174a3 3 0 0 0 0 5.652V14a1 1 0 0 0 2 0V6.824a3 3 0 0 0 0-5.648V4a1 1 0 0 1-2 0V1.174z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/TouchScreenButton.svg b/editor/icons/TouchScreenButton.svg
index 731743694d..183f414e9a 100644
--- a/editor/icons/TouchScreenButton.svg
+++ b/editor/icons/TouchScreenButton.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h2v-1h-1-1v-2h8v2h-2v1h2a1 1 0 0 0 1-1v-2a1 1 0 0 0 -1-1zm4 2a1 1 0 0 0 -1 1v7 .033203l-2.4746-1.8086c-.52015-.3803-1.1948-.4556-1.6504 0-.45566.4556-.45561 1.1948 0 1.6504l4.125 4.125h6c1.1046 0 2-.8954 2-2v-5h-6v-4a1 1 0 0 0 -1-1z" fill="#8da5f3"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 1a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2V4H3V2h8v2H9v1h2a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm5 7V4a1 1 0 0 0-2 0v7.05l-2.5-1.8A1 1 0 0 0 1.875 11L6 15h6a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2z" fill="#8da5f3"/></svg>
diff --git a/editor/icons/TrackCapture.svg b/editor/icons/TrackCapture.svg
index b3d5f09eff..6251330b12 100644
--- a/editor/icons/TrackCapture.svg
+++ b/editor/icons/TrackCapture.svg
@@ -1 +1 @@
-<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e1da5b"><path d="m2.1665128.99764963c-.422625 0-.763672.34104737-.763672.76367187v4.5742187c0 .4226242.341047.7617192.763672.7617192h4.472656c.422625 0 .763672-.339095.763672-.7617192v-.9882812h-3.300781c-.1662 0-.298828-.3390943-.298828-.7617188v-1.2246094c0-.4226244.132628-.7636718.298828-.7636718h3.300781v-.8359375c0-.4226245-.341047-.76367187-.763672-.76367187z"/><path d="m9.1827441 4.7953408c.5166221-1.0415625 1.0955249-2.2117429 1.2864509-2.600401l.347137-.7066511.679654.00665.679654.00665.956945 2.3125c.526319 1.271875 1.007254 2.4334375 1.068744 2.5812497l.1118.26875h-.597215-.597214l-.332849-.6437497-.332849-.64375h-1.133826-1.133825l-.3786749.6561133-.3786747.6561134-.5922856.000137-.592285.000136zm3.1779349-.369483c.0042-.00346-.233487-.4884588-.528245-1.0777779l-.535922-1.0714891-.03691.0875c-.0203.048125-.183516.425-.362699.8375-.179182.4125-.355738.85125-.392346.975-.03661.12375-.07127.2390723-.07703.2562715-.0083.024853.188215.027989.957503.015278.532385-.0088.971429-.018823.975651-.022283z" stroke="#e1da5b" stroke-width=".803"/></g></svg>
+<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-.75H3.4c-.3 0-.4-.2-.4-1v-.5c0-.8.1-1 .4-1H7V2a1 1 0 0 0-1-1zm5.5 6h2l.75-1.5h2L13 7h2l-3-6h-1.5zm3.75-4 .5 1h-1z" fill="#e1da5b"/></svg>
diff --git a/editor/icons/TrackContinuous.svg b/editor/icons/TrackContinuous.svg
index 951a13a28e..b48e67af76 100644
--- a/editor/icons/TrackContinuous.svg
+++ b/editor/icons/TrackContinuous.svg
@@ -1 +1 @@
-<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 6c6 0 6-4 12-4" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></svg>
+<svg height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 6c6 0 6-4 12-4" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-width="2"/></svg>
diff --git a/editor/icons/TransitionImmediate.svg b/editor/icons/TransitionImmediate.svg
index 638675a349..b39aaa77c4 100644
--- a/editor/icons/TransitionImmediate.svg
+++ b/editor/icons/TransitionImmediate.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2.988 3c-.547.01-.987.451-.988.998v8a1 1 0 0 0 1.555.832l6-4a1 1 0 0 0 0-1.664l-6-4A1 1 0 0 0 2.988 3z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 12a1 1 0 0 0 1.555.832l6-4a1 1 0 0 0 0-1.664l-6-4A1 1 0 0 0 2 4z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/TransitionImmediateAuto.svg b/editor/icons/TransitionImmediateAuto.svg
index a4ee688469..6118e86250 100644
--- a/editor/icons/TransitionImmediateAuto.svg
+++ b/editor/icons/TransitionImmediateAuto.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2.988 3c-.547.01-.987.451-.988.998v8a1 1 0 0 0 1.555.832l6-4a1 1 0 0 0 0-1.664l-6-4A1 1 0 0 0 2.988 3z" fill="#77ce57"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 12a1 1 0 0 0 1.555.832l6-4a1 1 0 0 0 0-1.664l-6-4A1 1 0 0 0 2 4z" fill="#77ce57"/></svg>
diff --git a/editor/icons/Transpose.svg b/editor/icons/Transpose.svg
deleted file mode 100644
index 41b88ea667..0000000000
--- a/editor/icons/Transpose.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v14h7v-7h7v-7zm2 2h3v3h-3zm0 5h3v5h-3zm12 2-5 5h5z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/Tree.svg b/editor/icons/Tree.svg
index a6c8ace55f..2316bd9498 100644
--- a/editor/icons/Tree.svg
+++ b/editor/icons/Tree.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v13c.0000552.55226.44774.99994 1 1h13v-2h-12v-6h2v3c.0000552.55226.44774.99994 1 1h9v-2h-8v-2h8v-2h-12v-2h12v-2z" fill="#8eef97"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 1v13a1 1 0 0 0 1 1h13v-2H3V7h2v3a1 1 0 0 0 1 1h9V9H7V7h8V5H3V3h12V1z" fill="#8eef97"/></svg>
diff --git a/editor/icons/VSlider.svg b/editor/icons/VSlider.svg
index 16fafe1162..f013e766d0 100644
--- a/editor/icons/VSlider.svg
+++ b/editor/icons/VSlider.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 1a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0 -2-2zm5 0c-.55228 0-1 .44772-1 1s.44772 1 1 1h2c.55228 0 1-.44772 1-1s-.44772-1-1-1zm-4 5.8672c-.32639.086294-.6624.13092-1 .13281-.33752-.0012549-.67352-.045224-1-.13086v5 1.1309 1c-.019125 1.3523 2.0191 1.3523 2 0v-1-1.1328-5zm5 .13281c-.55228 0-1 .44772-1 1s.44772 1 1 1 1-.44772 1-1-.44772-1-1-1zm-1 6c-.55228 0-1 .44772-1 1s.44772 1 1 1h2c.55228 0 1-.44772 1-1s-.44772-1-1-1z" fill="#8eef97"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M5 1a2 2 0 0 0 0 4 2 2 0 0 0 0-4zm5 0a1 1 0 0 0 0 2h2a1 1 0 0 0 0-2zM6 6.867a4 4 0 0 1-2 0v7.13a1 1 0 0 0 2 0zM11 7a1 1 0 1 0 0 2 1 1 0 0 0 0-2zm-1 6a1 1 0 0 0 0 2h2a1 1 0 0 0 0-2z" fill="#8eef97"/></svg>
diff --git a/editor/icons/VehicleBody3D.svg b/editor/icons/VehicleBody3D.svg
index 5e21f40c85..631f877cc5 100644
--- a/editor/icons/VehicleBody3D.svg
+++ b/editor/icons/VehicleBody3D.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 3a1 1 0 0 0 -1 1l-1 3h-2v4h1.0508c.23167-1.1411 1.2398-2 2.4492-2s2.2175.85893 2.4492 2h2.1016c.23167-1.1411 1.2398-2 2.4492-2s2.2175.85893 2.4492 2h1.0508v-4h-4v-4zm1 1h4v3h-4zm-1.5 6a1.5 1.5 0 0 0 -1.5 1.5 1.5 1.5 0 0 0 1.5 1.5 1.5 1.5 0 0 0 1.5-1.5 1.5 1.5 0 0 0 -1.5-1.5zm7 0a1.5 1.5 0 0 0 -1.5 1.5 1.5 1.5 0 0 0 1.5 1.5 1.5 1.5 0 0 0 1.5-1.5 1.5 1.5 0 0 0 -1.5-1.5z" fill="#fc7f7f"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M5 3a1 1 0 0 0-1 .75L3 7H1v4h1.05A2.5 2.5 0 0 1 4.5 9a2.5 2.5 0 0 1 2.45 2h2.1a2.5 2.5 0 0 1 2.45-2 2.5 2.5 0 0 1 2.45 2H15V7h-4V3zm1 1h4v3H6zm-1.5 6a1.5 1.5 0 0 0 0 3 1.5 1.5 0 0 0 0-3zm7 0a1.5 1.5 0 0 0 0 3 1.5 1.5 0 0 0 0-3z" fill="#fc7f7f"/></svg>
diff --git a/editor/icons/ViewportTexture.svg b/editor/icons/ViewportTexture.svg
index a5b1e4ab07..c3d97a8805 100644
--- a/editor/icons/ViewportTexture.svg
+++ b/editor/icons/ViewportTexture.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 2c-.5304.0000801-1.0391.21085-1.4141.58594-.37509.37501-.58586.88366-.58594 1.4141v8c.0000803.5304.21085 1.0391.58594 1.4141.37501.37509.88366.58586 1.4141.58594h10c1.1046 0 2-.89543 2-2v-8c0-1.1046-.89543-2-2-2h-10zm0 1h10c.55228.0000096.99999.44772 1 1v8c-.00001.55228-.44772.99999-1 1h-10c-.55228-.00001-.99999-.44772-1-1v-8c.0000096-.55228.44772-.99999 1-1zm6 3v1h-1v1h-2v1h-1v1h-1v1h2 2 2 2v-2h-1v-1-1h-1v-1z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H3zm0 1h10a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm6 3v1H8v1H6v1H5v1H4v1h8V9h-1V7h-1V6z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/VisualShaderNodeBooleanUniform.svg b/editor/icons/VisualShaderNodeBooleanUniform.svg
index b4a7043fb3..f31f945f55 100644
--- a/editor/icons/VisualShaderNodeBooleanUniform.svg
+++ b/editor/icons/VisualShaderNodeBooleanUniform.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 .02734375c-1.108 0-2 .892-2 2.00000005v1.9726562h2v2a3 3 0 0 1 2.5 1.3457031 3 3 0 0 1 2.5-1.3457031 3 3 0 0 1 2 .7675781 3 3 0 0 1 2-.7675781 3 3 0 0 1 2 .7695312v-2.7695312h2v5a1 1 0 0 0 1 1v-7.9726562c0-1.10800005-.892-2.00000005-2-2.00000005zm0 7.97265625v2a1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm5 0a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm4 0a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-6.5 2.654297a3 3 0 0 1 -2.5 1.345703h-2v2.027344c0 1.108.892 2 2 2h12c1.108 0 2-.892 2-2v-2.027344a3 3 0 0 1 -2.5-1.345703 3 3 0 0 1 -2.5 1.345703 3 3 0 0 1 -2-.767578 3 3 0 0 1 -2 .767578 3 3 0 0 1 -2.5-1.345703z" fill="#6f91f0" stroke-linecap="square" stroke-opacity=".75" stroke-width="2"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 0a2 2 0 0 0-2 2v2h2v2a3 3 0 0 1 2.5 1.346A3 3 0 0 1 9 6.768a3 3 0 0 1 4 0V4h2v5a1 1 0 0 0 1 1V2a2 2 0 0 0-2-2zm0 8v2a1 1 0 0 0 0-2zm5 0a1 1 0 0 0 0 2 1 1 0 0 0 0-2zm4 0a1 1 0 0 0 0 2 1 1 0 0 0 0-2zm-6.5 2.654A3 3 0 0 1 2 12H0v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2a3 3 0 0 1-2.5-1.346 3 3 0 0 1-4.5.578 3 3 0 0 1-4.5-.578z" fill="#6f91f0"/></svg>
diff --git a/editor/icons/VisualShaderNodeCubemap.svg b/editor/icons/VisualShaderNodeCubemap.svg
index 3cf870e711..e28552c36d 100644
--- a/editor/icons/VisualShaderNodeCubemap.svg
+++ b/editor/icons/VisualShaderNodeCubemap.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="M7 3.514 4.31 4.857 7 6.201l2.69-1.344zM3.428 6.012v2.69l2.857 1.427V7.44zm7.144 0-2.857 1.43v2.687L10.572 8.7z" fill="#eac968"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="M7 3.5 4 5l3 1.5L10 5zM3.5 6v3l2.9 1.45v-3zm7 0L7.6 7.45v3L10.5 9z" fill="#eac968"/></svg>
diff --git a/editor/icons/VisualShaderNodeExpression.svg b/editor/icons/VisualShaderNodeExpression.svg
index 710ba818b7..ecee759562 100644
--- a/editor/icons/VisualShaderNodeExpression.svg
+++ b/editor/icons/VisualShaderNodeExpression.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#cf68ea"><path d="m4.859536 3.0412379c-2.0539867 0-3.7190721 1.6650852-3.7190721 3.719072v6.1984521h2.4793814v-2.479381h2.4793814v-2.4793803h-2.4793814v-1.2396908c0-.6846622.5550285-1.2396907 1.2396907-1.2396907h1.2396907v-2.4793813z"/><path d="m7.5889175 3.0000003 2.5000005 4.9999997-2.5000005 5h2.5000005l1.135249-2.727 1.36475 2.727h2.499999l-2.499999-5 2.499999-4.9999997h-2.499999l-1.13525 2.7269998-1.364749-2.7269998zm7.4999985 9.9999997v-6.25z"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.86 3.041a3.72 3.72 0 0 0-3.72 3.72v6.198h2.48v-2.48H6.1V8H3.62V6.76a1.24 1.24 0 0 1 1.24-1.24H6.1V3.042zM7.589 3l2.5 5-2.5 5h2.5l1.135-2.727L12.59 13h2.5l-2.5-5 2.5-5h-2.5l-1.135 2.727L10.089 3z" fill="#cf68ea"/></svg>
diff --git a/editor/icons/VisualShaderNodeGlobalExpression.svg b/editor/icons/VisualShaderNodeGlobalExpression.svg
index 0cafffb152..5e967ea571 100644
--- a/editor/icons/VisualShaderNodeGlobalExpression.svg
+++ b/editor/icons/VisualShaderNodeGlobalExpression.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#35d4f4"><path d="m4.859536 3.0412379c-2.0539867 0-3.7190721 1.6650852-3.7190721 3.719072v6.1984521h2.4793814v-2.479381h2.4793814v-2.4793803h-2.4793814v-1.2396908c0-.6846622.5550285-1.2396907 1.2396907-1.2396907h1.2396907v-2.4793813z"/><path d="m7.5889175 3.0000003 2.5000005 4.9999997-2.5000005 5h2.5000005l1.135249-2.727 1.36475 2.727h2.499999l-2.499999-5 2.499999-4.9999997h-2.499999l-1.13525 2.7269998-1.364749-2.7269998zm7.4999985 9.9999997v-6.25z"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.86 3.041a3.72 3.72 0 0 0-3.72 3.72v6.198h2.48v-2.48H6.1V8H3.62V6.76a1.24 1.24 0 0 1 1.24-1.24H6.1V3.042zM7.589 3l2.5 5-2.5 5h2.5l1.135-2.727L12.59 13h2.5l-2.5-5 2.5-5h-2.5l-1.135 2.727L10.089 3z" fill="#35d4f4"/></svg>
diff --git a/editor/icons/VisualShaderNodeTransformUniform.svg b/editor/icons/VisualShaderNodeTransformUniform.svg
index 5d3e6977e0..507b616eff 100644
--- a/editor/icons/VisualShaderNodeTransformUniform.svg
+++ b/editor/icons/VisualShaderNodeTransformUniform.svg
@@ -1 +1 @@
-<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m2 0c-1.1045695 0-2 .8954-2 2v10c0 1.1046.8954305 2 2 2h10c1.104569 0 2-.8954 2-2v-10c0-1.1046-.895431-2-2-2zm-1 1h1 2v1h-2v10h2v1h-2-1v-1-10zm9 0h3v11 1h-3v-1h2v-10h-2zm-.0292969 2a1.0001 1.0001 0 0 1 1.0292969 1v7h-2v-4.5859375l-1.2929688 1.2929687a1.0001 1.0001 0 0 1 -1.4140624 0l-1.2929688-1.2929687v4.5859375h-2v-7a1.0001 1.0001 0 0 1 .984375-.9980469 1.0001 1.0001 0 0 1 .7226562.2910157l2.2929688 2.2929687 2.2929688-2.2929687a1.0001 1.0001 0 0 1 .6777343-.2929688z" fill="#ea686c"/></svg>
+<svg height="14" viewBox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="M2 0a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zM1 1h3v1H2v10h2v1H1zm9 0h3v12h-3v-1h2V2h-2zm1 10H9V6.414L7.707 7.707a1 1 0 0 1-1.414 0L5 6.414V11H3V4a1 1 0 0 1 1.707-.707L7 5.586l2.293-2.293A1 1 0 0 1 11 4z" fill="#ea686c"/></svg>
diff --git a/editor/icons/VoxelGIData.svg b/editor/icons/VoxelGIData.svg
index 51b66ca899..a4eb17418d 100644
--- a/editor/icons/VoxelGIData.svg
+++ b/editor/icons/VoxelGIData.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h4v-2H3V3h9V1zm2 3v2h2V4zm7 0a4 4 0 0 0-2 7.459V12a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-.541A4 4 0 0 0 11 4zm0 2a2 2 0 0 1 0 4 2 2 0 0 1 0-4zM4 7v2h2V7zm0 3v2h2v-2zm6 4v1h2v-1z" fill="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h4v-2H3V3h9V1zm7 11a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-.541a4 4 0 1 0-4 0zm2-6a2 2 0 0 1 0 4 2 2 0 0 1 0-4zm-1 8v1h2v-1zM4 4h2v2H4zm0 3h2v2H4zm0 3h2v2H4z" fill="#e0e0e0"/></svg>
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_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp
index cbd00f0f6b..8e04ce4c7e 100644
--- a/editor/import/resource_importer_imagefont.cpp
+++ b/editor/import/resource_importer_imagefont.cpp
@@ -111,7 +111,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
int chr_width = chr_cell_width - char_margin.position.x - char_margin.size.x;
int chr_height = chr_cell_height - char_margin.position.y - char_margin.size.y;
- ERR_FAIL_COND_V_MSG(chr_width <= 0 || chr_height <= 0, ERR_FILE_CANT_READ, TTR("Character margin too bit."));
+ ERR_FAIL_COND_V_MSG(chr_width <= 0 || chr_height <= 0, ERR_FILE_CANT_READ, TTR("Character margin too big."));
Ref<FontFile> font;
font.instantiate();
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_scene.cpp b/editor/import/resource_importer_scene.cpp
index 677b2e78bd..48fc20adf0 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -1248,6 +1248,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
col->set_owner(p_node->get_owner());
col->set_transform(get_collision_shapes_transform(node_settings));
col->set_position(p_applied_root_scale * col->get_position());
+ const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
+ if (!pmo.is_null()) {
+ col->set_physics_material_override(pmo);
+ }
base = col;
} break;
case MESH_PHYSICS_RIGID_BODY_AND_MESH: {
@@ -1260,6 +1264,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
mi->set_transform(Transform3D());
rigid_body->add_child(mi, true);
mi->set_owner(rigid_body->get_owner());
+ const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
+ if (!pmo.is_null()) {
+ rigid_body->set_physics_material_override(pmo);
+ }
base = rigid_body;
} break;
case MESH_PHYSICS_STATIC_COLLIDER_ONLY: {
@@ -1271,6 +1279,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
p_node->set_owner(nullptr);
memdelete(p_node);
p_node = col;
+ const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
+ if (!pmo.is_null()) {
+ col->set_physics_material_override(pmo);
+ }
base = col;
} break;
case MESH_PHYSICS_AREA_ONLY: {
@@ -1287,6 +1299,9 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
} break;
}
+ base->set_collision_layer(node_settings["physics/layer"]);
+ base->set_collision_mask(node_settings["physics/mask"]);
+
for (const Ref<Shape3D> &E : shapes) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
cshape->set_shape(E);
@@ -1605,6 +1620,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/body_type", PROPERTY_HINT_ENUM, "Static,Dynamic,Area"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/shape_type", PROPERTY_HINT_ENUM, "Decompose Convex,Simple Convex,Trimesh,Box,Sphere,Cylinder,Capsule", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "physics/physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), Variant()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), 1));
// Decomposition
Ref<MeshConvexDecompositionSettings> decomposition_default = Ref<MeshConvexDecompositionSettings>();
@@ -1703,9 +1721,7 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
p_options.has("generate/physics") &&
p_options["generate/physics"].operator bool();
- if (
- p_option == "physics/body_type" ||
- p_option == "physics/shape_type") {
+ if (p_option.find("physics/") >= 0) {
// Show if need to generate collisions.
return generate_physics;
}
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index e81e836e9e..114ba5653a 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());
@@ -417,7 +418,7 @@ void ResourceImporterTexture::_save_editor_meta(const Dictionary &p_metadata, co
Dictionary ResourceImporterTexture::_load_editor_meta(const String &p_path) const {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(f.is_null(), Dictionary(), "Missing required editor-specific import metadata for a texture; please, reimport.");
+ ERR_FAIL_COND_V_MSG(f.is_null(), Dictionary(), vformat("Missing required editor-specific import metadata for a texture (please reimport it using the 'Import' tab): '%s'", p_path));
return f->get_var();
}
@@ -789,11 +790,16 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co
ResourceImporterTexture *ResourceImporterTexture::singleton = nullptr;
ResourceImporterTexture::ResourceImporterTexture() {
- singleton = this;
+ if (!singleton) {
+ singleton = this;
+ }
CompressedTexture2D::request_3d_callback = _texture_reimport_3d;
CompressedTexture2D::request_roughness_callback = _texture_reimport_roughness;
CompressedTexture2D::request_normal_callback = _texture_reimport_normal;
}
ResourceImporterTexture::~ResourceImporterTexture() {
+ if (singleton == this) {
+ singleton = nullptr;
+ }
}
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/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp
index 92d287c54f..2142ee3c10 100644
--- a/editor/import/scene_import_settings.cpp
+++ b/editor/import/scene_import_settings.cpp
@@ -64,7 +64,7 @@ class SceneImportSettingsData : public Object {
current[p_name] = p_value;
- // SceneImportSettings must decide if a new collider should be generated or not
+ // SceneImportSettings must decide if a new collider should be generated or not.
if (category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE) {
SceneImportSettings::get_singleton()->request_generate_collider();
}
@@ -350,8 +350,12 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE;
} else if (Object::cast_to<AnimationPlayer>(p_node)) {
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
+
+ animation_player = Object::cast_to<AnimationPlayer>(p_node);
+ animation_player->connect(SNAME("animation_finished"), callable_mp(this, &SceneImportSettings::_animation_finished));
} else if (Object::cast_to<Skeleton3D>(p_node)) {
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;
+ skeletons.push_back(Object::cast_to<Skeleton3D>(p_node));
} else {
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
}
@@ -413,7 +417,7 @@ void SceneImportSettings::_update_scene() {
material_tree->clear();
mesh_tree->clear();
- //hidden roots
+ // Hidden roots.
material_tree->create_item();
mesh_tree->create_item();
@@ -432,7 +436,7 @@ void SceneImportSettings::_update_view_gizmos() {
MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(e.value.node);
if (mesh_node == nullptr || mesh_node->get_mesh().is_null()) {
- // Nothing to do
+ // Nothing to do.
continue;
}
@@ -591,7 +595,7 @@ void SceneImportSettings::open_settings(const String &p_path, bool p_for_animati
scene_import_settings_data->settings = nullptr;
scene_import_settings_data->path = p_path;
- // Visibility
+ // Visibility.
data_mode->set_tab_hidden(1, p_for_animation);
data_mode->set_tab_hidden(2, p_for_animation);
if (p_for_animation) {
@@ -691,12 +695,13 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
scene_import_settings_data->hide_options = false;
if (p_type == "Node") {
- node_selected->hide(); //always hide just in case
+ node_selected->hide(); // Always hide just in case.
mesh_preview->hide();
+ _reset_animation();
+
if (Object::cast_to<Node3D>(scene)) {
Object::cast_to<Node3D>(scene)->show();
}
- //NodeData &nd=node_map[p_id];
material_tree->deselect_all();
mesh_tree->deselect_all();
NodeData &nd = node_map[p_id];
@@ -734,12 +739,13 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
}
}
} else if (p_type == "Animation") {
- node_selected->hide(); //always hide just in case
+ node_selected->hide(); // Always hide just in case.
mesh_preview->hide();
+ _reset_animation(p_id);
+
if (Object::cast_to<Node3D>(scene)) {
Object::cast_to<Node3D>(scene)->show();
}
- //NodeData &nd=node_map[p_id];
material_tree->deselect_all();
mesh_tree->deselect_all();
AnimationData &ad = animation_map[p_id];
@@ -768,6 +774,7 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
mesh_preview->set_mesh(md.mesh);
mesh_preview->show();
+ _reset_animation();
material_tree->deselect_all();
@@ -780,6 +787,7 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
}
mesh_preview->show();
+ _reset_animation();
MaterialData &md = material_map[p_id];
@@ -836,7 +844,7 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
if (scene_import_settings_data->settings) {
for (const ResourceImporter::ImportOption &E : options) {
scene_import_settings_data->defaults[E.option.name] = E.default_value;
- //needed for visibility toggling (fails if something is missing)
+ // Needed for visibility toggling (fails if something is missing).
if (scene_import_settings_data->settings->has(E.option.name)) {
scene_import_settings_data->current[E.option.name] = (*scene_import_settings_data->settings)[E.option.name];
} else {
@@ -850,6 +858,127 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
scene_import_settings_data->notify_property_list_changed();
}
+void SceneImportSettings::_inspector_property_edited(const String &p_name) {
+ if (p_name == "settings/loop_mode") {
+ if (!animation_map.has(selected_id)) {
+ return;
+ }
+ HashMap<StringName, Variant> settings = animation_map[selected_id].settings;
+ if (settings.has(p_name)) {
+ animation_loop_mode = static_cast<Animation::LoopMode>((int)settings[p_name]);
+ } else {
+ animation_loop_mode = Animation::LoopMode::LOOP_NONE;
+ }
+ }
+}
+
+void SceneImportSettings::_reset_bone_transforms() {
+ for (Skeleton3D *skeleton : skeletons) {
+ skeleton->reset_bone_poses();
+ }
+}
+
+void SceneImportSettings::_play_animation() {
+ if (animation_player == nullptr) {
+ return;
+ }
+ StringName id = StringName(selected_id);
+ if (animation_player->has_animation(id)) {
+ if (animation_player->is_playing()) {
+ animation_player->pause();
+ animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+ set_process(false);
+ } else {
+ animation_player->play(id);
+ animation_play_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
+ set_process(true);
+ }
+ }
+}
+
+void SceneImportSettings::_stop_current_animation() {
+ animation_pingpong = false;
+ animation_player->stop();
+ animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+ animation_slider->set_value_no_signal(0.0);
+ set_process(false);
+}
+
+void SceneImportSettings::_reset_animation(const String &p_animation_name) {
+ if (p_animation_name.is_empty()) {
+ animation_preview->hide();
+
+ if (animation_player != nullptr && animation_player->is_playing()) {
+ animation_player->stop();
+ }
+ animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+
+ _reset_bone_transforms();
+ set_process(false);
+ } else {
+ _reset_bone_transforms();
+ animation_preview->show();
+
+ animation_loop_mode = Animation::LoopMode::LOOP_NONE;
+ animation_pingpong = false;
+
+ if (animation_map.has(p_animation_name)) {
+ HashMap<StringName, Variant> settings = animation_map[p_animation_name].settings;
+ if (settings.has("settings/loop_mode")) {
+ animation_loop_mode = static_cast<Animation::LoopMode>((int)settings["settings/loop_mode"]);
+ }
+ }
+
+ if (animation_player->is_playing() && animation_loop_mode != Animation::LoopMode::LOOP_NONE) {
+ animation_player->play(p_animation_name);
+ } else {
+ animation_player->stop(true);
+ animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+ animation_player->set_assigned_animation(p_animation_name);
+ animation_player->seek(0.0, true);
+ animation_slider->set_value_no_signal(0.0);
+ set_process(false);
+ }
+ }
+}
+
+void SceneImportSettings::_animation_slider_value_changed(double p_value) {
+ if (animation_player == nullptr || !animation_map.has(selected_id) || animation_map[selected_id].animation.is_null()) {
+ return;
+ }
+ if (animation_player->is_playing()) {
+ animation_player->stop();
+ animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+ set_process(false);
+ }
+ animation_player->seek(p_value * animation_map[selected_id].animation->get_length(), true);
+}
+
+void SceneImportSettings::_animation_finished(const StringName &p_name) {
+ Animation::LoopMode loop_mode = animation_loop_mode;
+
+ switch (loop_mode) {
+ case Animation::LOOP_NONE: {
+ animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+ animation_slider->set_value_no_signal(1.0);
+ set_process(false);
+ } break;
+ case Animation::LOOP_LINEAR: {
+ animation_player->play(p_name);
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ if (animation_pingpong) {
+ animation_player->play(p_name);
+ } else {
+ animation_player->play_backwards(p_name);
+ }
+ animation_pingpong = !animation_pingpong;
+ } break;
+ default: {
+ } break;
+ }
+}
+
void SceneImportSettings::_material_tree_selected() {
if (selecting) {
return;
@@ -884,6 +1013,15 @@ void SceneImportSettings::_scene_tree_selected() {
_select(scene_tree, type, import_id);
}
+void SceneImportSettings::_cleanup() {
+ skeletons.clear();
+ if (animation_player != nullptr) {
+ animation_player->disconnect(SNAME("animation_finished"), callable_mp(this, &SceneImportSettings::_animation_finished));
+ animation_player = nullptr;
+ }
+ set_process(false);
+}
+
void SceneImportSettings::_viewport_input(const Ref<InputEvent> &p_input) {
float *rot_x = &cam_rot_x;
float *rot_y = &cam_rot_y;
@@ -927,7 +1065,7 @@ void SceneImportSettings::_viewport_input(const Ref<InputEvent> &p_input) {
void SceneImportSettings::_re_import() {
HashMap<StringName, Variant> main_settings;
- main_settings = defaults;
+ main_settings = scene_import_settings_data->current;
main_settings.erase("_subresources");
Dictionary nodes;
Dictionary materials;
@@ -1005,6 +1143,25 @@ void SceneImportSettings::_notification(int p_what) {
action_menu->add_theme_style_override("normal", get_theme_stylebox("normal", "Button"));
action_menu->add_theme_style_override("hover", get_theme_stylebox("hover", "Button"));
action_menu->add_theme_style_override("pressed", get_theme_stylebox("pressed", "Button"));
+
+ if (animation_player != nullptr && animation_player->is_playing()) {
+ animation_play_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
+ } else {
+ animation_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+ }
+ animation_stop_button->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
+ } break;
+
+ case NOTIFICATION_PROCESS: {
+ if (animation_player != nullptr) {
+ animation_slider->set_value_no_signal(animation_player->get_current_animation_position() / animation_player->get_current_animation_length());
+ }
+ } break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (!is_visible()) {
+ _cleanup();
+ }
} break;
}
}
@@ -1331,16 +1488,53 @@ SceneImportSettings::SceneImportSettings() {
material_tree->set_hide_root(true);
+ VBoxContainer *vp_vb = memnew(VBoxContainer);
+ vp_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ vp_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ vp_vb->set_anchors_and_offsets_preset(Control::LayoutPreset::PRESET_FULL_RECT);
+ property_split->add_child(vp_vb);
+
SubViewportContainer *vp_container = memnew(SubViewportContainer);
- vp_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ vp_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vp_container->set_custom_minimum_size(Size2(10, 10));
vp_container->set_stretch(true);
vp_container->connect("gui_input", callable_mp(this, &SceneImportSettings::_viewport_input));
- property_split->add_child(vp_container);
+ vp_vb->add_child(vp_container);
base_viewport = memnew(SubViewport);
vp_container->add_child(base_viewport);
+ animation_preview = memnew(PanelContainer);
+ animation_preview->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ vp_vb->add_child(animation_preview);
+ animation_preview->hide();
+
+ HBoxContainer *animation_hbox = memnew(HBoxContainer);
+ animation_preview->add_child(animation_hbox);
+
+ animation_play_button = memnew(Button);
+ animation_hbox->add_child(animation_play_button);
+ animation_play_button->set_flat(true);
+ animation_play_button->set_focus_mode(Control::FOCUS_NONE);
+ animation_play_button->set_shortcut(ED_SHORTCUT("scene_import_settings/play_selected_animation", TTR("Selected Animation Play/Pause"), Key::SPACE));
+ animation_play_button->connect(SNAME("pressed"), callable_mp(this, &SceneImportSettings::_play_animation));
+
+ animation_stop_button = memnew(Button);
+ animation_hbox->add_child(animation_stop_button);
+ animation_stop_button->set_flat(true);
+ animation_stop_button->set_focus_mode(Control::FOCUS_NONE);
+ animation_stop_button->connect(SNAME("pressed"), callable_mp(this, &SceneImportSettings::_stop_current_animation));
+
+ animation_slider = memnew(HSlider);
+ animation_hbox->add_child(animation_slider);
+ animation_slider->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ animation_slider->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ animation_slider->set_max(1.0);
+ animation_slider->set_step(1.0 / 100.0);
+ animation_slider->set_value_no_signal(0.0);
+ animation_slider->set_focus_mode(Control::FOCUS_NONE);
+ animation_slider->connect(SNAME("value_changed"), callable_mp(this, &SceneImportSettings::_animation_slider_value_changed));
+
base_viewport->set_use_own_world_3d(true);
camera = memnew(Camera3D);
@@ -1406,6 +1600,7 @@ SceneImportSettings::SceneImportSettings() {
inspector = memnew(EditorInspector);
inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
+ inspector->connect(SNAME("property_edited"), callable_mp(this, &SceneImportSettings::_inspector_property_edited));
property_split->add_child(inspector);
diff --git a/editor/import/scene_import_settings.h b/editor/import/scene_import_settings.h
index d65bb1404a..b4954b9099 100644
--- a/editor/import/scene_import_settings.h
+++ b/editor/import/scene_import_settings.h
@@ -35,10 +35,13 @@
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/skeleton_3d.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
+#include "scene/gui/panel_container.h"
+#include "scene/gui/slider.h"
#include "scene/gui/split_container.h"
#include "scene/gui/subviewport_container.h"
#include "scene/gui/tab_container.h"
@@ -85,6 +88,15 @@ class SceneImportSettings : public ConfirmationDialog {
MeshInstance3D *mesh_preview = nullptr;
Ref<SphereMesh> material_preview;
+ AnimationPlayer *animation_player = nullptr;
+ List<Skeleton3D *> skeletons;
+ PanelContainer *animation_preview = nullptr;
+ HSlider *animation_slider = nullptr;
+ Button *animation_play_button = nullptr;
+ Button *animation_stop_button = nullptr;
+ Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE;
+ bool animation_pingpong = false;
+
Ref<StandardMaterial3D> collider_mat;
float cam_rot_x = 0.0f;
@@ -151,9 +163,17 @@ class SceneImportSettings : public ConfirmationDialog {
void _update_view_gizmos();
void _update_camera();
void _select(Tree *p_from, String p_type, String p_id);
+ void _inspector_property_edited(const String &p_name);
+ void _reset_bone_transforms();
+ void _play_animation();
+ void _stop_current_animation();
+ void _reset_animation(const String &p_animation_name = "");
+ void _animation_slider_value_changed(double p_value);
+ void _animation_finished(const StringName &p_name);
void _material_tree_selected();
void _mesh_tree_selected();
void _scene_tree_selected();
+ void _cleanup();
void _viewport_input(const Ref<InputEvent> &p_input);
diff --git a/editor/import_defaults_editor.cpp b/editor/import_defaults_editor.cpp
index ebbea827c0..98a9bfe9dc 100644
--- a/editor/import_defaults_editor.cpp
+++ b/editor/import_defaults_editor.cpp
@@ -157,6 +157,9 @@ void ImportDefaultsEditor::_update_importer() {
settings->notify_property_list_changed();
+ // Set the importer class to fetch the correct class in the XML class reference.
+ // This allows tooltips to display when hovering properties.
+ inspector->set_object_class(importer->get_class_name());
inspector->edit(settings);
}
@@ -210,9 +213,14 @@ ImportDefaultsEditor::ImportDefaultsEditor() {
reset_defaults->connect("pressed", callable_mp(this, &ImportDefaultsEditor::_reset));
hb->add_child(reset_defaults);
add_child(hb);
+
inspector = memnew(EditorInspector);
add_child(inspector);
inspector->set_v_size_flags(SIZE_EXPAND_FILL);
+ // Make it possible to display tooltips stored in the XML class reference.
+ // The object name is set when the importer changes in `_update_importer()`.
+ inspector->set_use_doc_hints(true);
+
CenterContainer *cc = memnew(CenterContainer);
save_defaults = memnew(Button);
save_defaults->set_text(TTR("Save"));
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index 7b8b9cd7a4..a17f497725 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_resource_preview.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
class ImportDockParameters : public Object {
GDCLASS(ImportDockParameters, Object);
@@ -157,6 +158,13 @@ void ImportDock::_add_keep_import_option(const String &p_importer_name) {
}
void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_config) {
+ // Set the importer class to fetch the correct class in the XML class reference.
+ // This allows tooltips to display when hovering properties.
+ if (params->importer != nullptr) {
+ // Null check to avoid crashing if the "Keep File (No Import)" mode is selected.
+ import_opts->set_object_class(params->importer->get_class_name());
+ }
+
List<ResourceImporter::ImportOption> options;
if (params->importer.is_valid()) {
@@ -459,7 +467,6 @@ static bool _find_owners(EditorFileSystemDirectory *efsd, const String &p_path)
}
void ImportDock::_reimport_attempt() {
- bool need_restart = false;
bool used_in_resources = false;
String importer_name;
@@ -476,14 +483,15 @@ void ImportDock::_reimport_attempt() {
String imported_with = config->get_value("remap", "importer");
if (imported_with != importer_name) {
- need_restart = true;
+ need_cleanup.push_back(params->paths[i]);
if (_find_owners(EditorFileSystem::get_singleton()->get_filesystem(), params->paths[i])) {
used_in_resources = true;
}
}
}
- if (need_restart) {
+ if (!need_cleanup.is_empty() || used_in_resources) {
+ cleanup_warning->set_visible(!need_cleanup.is_empty());
label_warning->set_visible(used_in_resources);
reimport_confirm->popup_centered();
return;
@@ -492,11 +500,42 @@ void ImportDock::_reimport_attempt() {
_reimport();
}
-void ImportDock::_reimport_and_restart() {
- EditorNode::get_singleton()->save_all_scenes();
- EditorResourcePreview::get_singleton()->stop(); //don't try to re-create previews after import
+void ImportDock::_reimport_and_cleanup() {
+ HashMap<String, Ref<Resource>> old_resources;
+
+ for (const String &path : need_cleanup) {
+ Ref<Resource> res = ResourceLoader::load(path);
+ res->set_path("");
+ res->set_meta(SNAME("_skip_save_"), true);
+ old_resources[path] = res;
+ }
+
+ EditorResourcePreview::get_singleton()->stop(); // Don't try to re-create previews after import.
_reimport();
- EditorNode::get_singleton()->restart_editor();
+
+ if (need_cleanup.is_empty()) {
+ return;
+ }
+
+ // After changing resource type we need to make sure that all old instances are unloaded or replaced.
+ EditorNode::get_singleton()->push_item(nullptr);
+ EditorUndoRedoManager::get_singleton()->clear_history();
+
+ List<Ref<Resource>> external_resources;
+ ResourceCache::get_cached_resources(&external_resources);
+
+ for (const String &path : need_cleanup) {
+ Ref<Resource> old_res = old_resources[path];
+ Ref<Resource> new_res = ResourceLoader::load(path);
+
+ for (int j = 0; j < EditorNode::get_editor_data().get_edited_scene_count(); j++) {
+ _replace_resource_in_object(EditorNode::get_editor_data().get_edited_scene_root(j), old_res, new_res);
+ }
+ for (Ref<Resource> res : external_resources) {
+ _replace_resource_in_object(res.ptr(), old_res, new_res);
+ }
+ }
+ need_cleanup.clear();
}
void ImportDock::_advanced_options() {
@@ -561,6 +600,37 @@ void ImportDock::_reimport() {
_set_dirty(false);
}
+void ImportDock::_replace_resource_in_object(Object *p_object, const Ref<Resource> &old_resource, const Ref<Resource> &new_resource) {
+ ERR_FAIL_NULL(p_object);
+
+ List<PropertyInfo> props;
+ p_object->get_property_list(&props);
+
+ for (const PropertyInfo &p : props) {
+ if (p.type != Variant::OBJECT || p.hint != PROPERTY_HINT_RESOURCE_TYPE) {
+ continue;
+ }
+
+ Ref<Resource> res = p_object->get(p.name);
+ if (res.is_null()) {
+ continue;
+ }
+
+ if (res == old_resource) {
+ p_object->set(p.name, new_resource);
+ } else {
+ _replace_resource_in_object(res.ptr(), old_resource, new_resource);
+ }
+ }
+
+ Node *n = Object::cast_to<Node>(p_object);
+ if (n) {
+ for (int i = 0; i < n->get_child_count(); i++) {
+ _replace_resource_in_object(n->get_child(i), old_resource, new_resource);
+ }
+ }
+}
+
void ImportDock::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
@@ -644,6 +714,9 @@ ImportDock::ImportDock() {
import_opts->set_v_size_flags(SIZE_EXPAND_FILL);
import_opts->connect("property_edited", callable_mp(this, &ImportDock::_property_edited));
import_opts->connect("property_toggled", callable_mp(this, &ImportDock::_property_toggled));
+ // Make it possible to display tooltips stored in the XML class reference.
+ // The object name is set when the importer changes in `_update_options()`.
+ import_opts->set_use_doc_hints(true);
hb = memnew(HBoxContainer);
content->add_child(hb);
@@ -673,13 +746,13 @@ ImportDock::ImportDock() {
advanced->connect("pressed", callable_mp(this, &ImportDock::_advanced_options));
reimport_confirm = memnew(ConfirmationDialog);
- reimport_confirm->set_ok_button_text(TTR("Save Scenes, Re-Import, and Restart"));
content->add_child(reimport_confirm);
- reimport_confirm->connect("confirmed", callable_mp(this, &ImportDock::_reimport_and_restart));
+ reimport_confirm->connect("confirmed", callable_mp(this, &ImportDock::_reimport_and_cleanup));
VBoxContainer *vbc_confirm = memnew(VBoxContainer());
- vbc_confirm->add_child(memnew(Label(TTR("Changing the type of an imported file requires editor restart."))));
- label_warning = memnew(Label(TTR("WARNING: Assets exist that use this resource, they may stop loading properly.")));
+ cleanup_warning = memnew(Label(TTR("The imported resource is currently loaded. All instances will be replaced and undo history will be cleared.")));
+ vbc_confirm->add_child(cleanup_warning);
+ label_warning = memnew(Label(TTR("WARNING: Assets exist that use this resource. They may stop loading properly after changing type.")));
vbc_confirm->add_child(label_warning);
reimport_confirm->add_child(vbc_confirm);
diff --git a/editor/import_dock.h b/editor/import_dock.h
index f627cd965d..07c54f8beb 100644
--- a/editor/import_dock.h
+++ b/editor/import_dock.h
@@ -54,8 +54,10 @@ class ImportDock : public VBoxContainer {
HashMap<StringName, Variant> property_values;
ConfirmationDialog *reimport_confirm = nullptr;
+ Label *cleanup_warning = nullptr;
Label *label_warning = nullptr;
Button *import = nullptr;
+ List<String> need_cleanup;
Control *advanced_spacer = nullptr;
Button *advanced = nullptr;
@@ -75,9 +77,11 @@ class ImportDock : public VBoxContainer {
void _property_toggled(const StringName &p_prop, bool p_checked);
void _set_dirty(bool p_dirty);
void _reimport_attempt();
- void _reimport_and_restart();
+ void _reimport_and_cleanup();
void _reimport();
+ void _replace_resource_in_object(Object *p_object, const Ref<Resource> &old_resource, const Ref<Resource> &new_resource);
+
void _advanced_options();
enum {
ITEM_SET_AS_DEFAULT = 100,
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index ea210872f7..8f2839ddb0 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++) {
@@ -125,7 +126,7 @@ void AnimationNodeBlendTreeEditor::update_graph() {
visible_properties.clear();
- graph->set_scroll_ofs(blend_tree->get_graph_offset() * EDSCALE);
+ graph->set_scroll_offset(blend_tree->get_graph_offset() * EDSCALE);
graph->clear_connections();
//erase all nodes
@@ -347,7 +348,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
return;
}
- Point2 instance_pos = graph->get_scroll_ofs();
+ Point2 instance_pos = graph->get_scroll_offset();
if (use_position_from_popup_menu) {
instance_pos += position_from_popup_menu;
} else {
@@ -1091,13 +1092,13 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
graph->set_connection_lines_curvature(graph_lines_curvature);
VSeparator *vs = memnew(VSeparator);
- graph->get_zoom_hbox()->add_child(vs);
- graph->get_zoom_hbox()->move_child(vs, 0);
+ graph->get_menu_hbox()->add_child(vs);
+ graph->get_menu_hbox()->move_child(vs, 0);
add_node = memnew(MenuButton);
- graph->get_zoom_hbox()->add_child(add_node);
+ graph->get_menu_hbox()->add_child(add_node);
add_node->set_text(TTR("Add Node..."));
- graph->get_zoom_hbox()->move_child(add_node, 0);
+ graph->get_menu_hbox()->move_child(add_node, 0);
add_node->get_popup()->connect("id_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_add_node));
add_node->get_popup()->connect("popup_hide", callable_mp(this, &AnimationNodeBlendTreeEditor::_popup_hide), CONNECT_DEFERRED);
add_node->connect("about_to_popup", callable_mp(this, &AnimationNodeBlendTreeEditor::_update_options_menu).bind(false));
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..0b2af0172c 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) {
@@ -1507,6 +1508,10 @@ void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) {
int base = 1;
String name = base_name;
while (state_machine->has_node(name)) {
+ if (name == prev_name) {
+ name_edit_popup->hide(); // The old name wins, the name doesn't change, just hide the popup.
+ return;
+ }
base++;
name = base_name + " " + itos(base);
}
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index eeb0fd5f66..5c26199af1 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -42,11 +42,7 @@
#include "editor/gui/editor_file_dialog.h"
#include "editor/project_settings_editor.h"
#include "scene/gui/menu_button.h"
-
-#include "modules/modules_enabled.gen.h" // For svg.
-#ifdef MODULE_SVG_ENABLED
-#include "modules/svg/image_loader_svg.h"
-#endif
+#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));
@@ -775,18 +771,9 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PackedB
image->copy_internals_from(Image::_webp_mem_loader_func(r, len));
} else if ((memcmp(&r[0], &bmp_signature[0], 2) == 0) && Image::_bmp_mem_loader_func) {
image->copy_internals_from(Image::_bmp_mem_loader_func(r, len));
+ } else if (Image::_svg_scalable_mem_loader_func) {
+ image->copy_internals_from(Image::_svg_scalable_mem_loader_func(r, len, 1.0));
}
-#ifdef MODULE_SVG_ENABLED
- else {
- ImageLoaderSVG svg_loader;
- Ref<Image> img = Ref<Image>(memnew(Image));
- Error err = svg_loader.create_image_from_utf8_buffer(img, image_data, 1.0, false);
-
- if (err == OK) {
- image->copy_internals_from(img);
- }
- }
-#endif
}
if (!image->is_empty()) {
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/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
index ef00af592f..7be0d6c172 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
@@ -217,11 +217,16 @@ void CPUParticles2DEditorPlugin::_generate_emission_mask() {
}
{
+ Vector2 offset;
+ if (emission_mask_centered->is_pressed()) {
+ offset = Vector2(-s.width * 0.5, -s.height * 0.5);
+ }
+
PackedVector2Array points;
points.resize(valid_positions.size());
Vector2 *pointsw = points.ptrw();
for (int i = 0; i < valid_positions.size(); i += 1) {
- pointsw[i] = valid_positions[i];
+ pointsw[i] = valid_positions[i] + offset;
}
particles->set_emission_points(points);
}
@@ -281,9 +286,14 @@ CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() {
emission_mask_mode->add_item(TTR("Solid Pixels"), EMISSION_MODE_SOLID);
emission_mask_mode->add_item(TTR("Border Pixels"), EMISSION_MODE_BORDER);
emission_mask_mode->add_item(TTR("Directed Border Pixels"), EMISSION_MODE_BORDER_DIRECTED);
+ VBoxContainer *optionsvb = memnew(VBoxContainer);
+ emvb->add_margin_child(TTR("Options"), optionsvb);
+ emission_mask_centered = memnew(CheckBox);
+ emission_mask_centered->set_text(TTR("Centered"));
+ optionsvb->add_child(emission_mask_centered);
emission_colors = memnew(CheckBox);
- emission_colors->set_text(TTR("Capture from Pixel"));
- emvb->add_margin_child(TTR("Emission Colors"), emission_colors);
+ emission_colors->set_text(TTR("Capture Colors from Pixel"));
+ optionsvb->add_child(emission_colors);
toolbar->add_child(emission_mask);
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h
index cab9fca4d6..ff8e171208 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.h
@@ -69,6 +69,7 @@ class CPUParticles2DEditorPlugin : public EditorPlugin {
ConfirmationDialog *emission_mask = nullptr;
OptionButton *emission_mask_mode = nullptr;
+ CheckBox *emission_mask_centered = nullptr;
CheckBox *emission_colors = nullptr;
String source_emission_file;
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index a1a692bdd1..6228faaa72 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));
}
@@ -184,7 +184,9 @@ void CurveEdit::gui_input(const Ref<InputEvent> &p_event) {
toggle_linear(selected_index, selected_tangent_index);
} else {
int point_to_remove = get_point_at(mpos);
- if (point_to_remove != -1) {
+ if (point_to_remove == -1) {
+ set_selected_index(-1); // Nothing on the place of the click, just deselect the point.
+ } else {
if (grabbing == GRAB_ADD) {
curve->remove_point(point_to_remove); // Point is temporary, so remove directly from curve.
set_selected_index(-1);
diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp
index 3068ad3f93..3c9bc991d3 100644
--- a/editor/plugins/debugger_editor_plugin.cpp
+++ b/editor/plugins/debugger_editor_plugin.cpp
@@ -54,8 +54,7 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) {
EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
- // Add separation for the warning/error icon that is displayed later.
- db->add_theme_constant_override("h_separation", 6 * EDSCALE);
+ db->set_theme_type_variation("BottomPanelButton");
debugger->set_tool_button(db);
// Main editor debug menu.
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/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp
index c3d1e920d7..5d90a746f9 100644
--- a/editor/plugins/font_config_plugin.cpp
+++ b/editor/plugins/font_config_plugin.cpp
@@ -1020,6 +1020,7 @@ EditorPropertyFontNamesArray::EditorPropertyFontNamesArray() {
if (OS::get_singleton()) {
Vector<String> fonts = OS::get_singleton()->get_system_fonts();
+ fonts.sort();
for (int i = 0; i < fonts.size(); i++) {
menu->add_item(fonts[i], i + 6);
}
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..03d3ac1732 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) {
@@ -292,11 +293,16 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() {
texdata.resize(w * h * 2 * sizeof(float));
{
+ Vector2 offset;
+ if (emission_mask_centered->is_pressed()) {
+ offset = Vector2(-s.width * 0.5, -s.height * 0.5);
+ }
+
uint8_t *tw = texdata.ptrw();
float *twf = reinterpret_cast<float *>(tw);
for (int i = 0; i < vpc; i++) {
- twf[i * 2 + 0] = valid_positions[i].x;
- twf[i * 2 + 1] = valid_positions[i].y;
+ twf[i * 2 + 0] = valid_positions[i].x + offset.x;
+ twf[i * 2 + 1] = valid_positions[i].y + offset.y;
}
}
@@ -417,9 +423,14 @@ GPUParticles2DEditorPlugin::GPUParticles2DEditorPlugin() {
emission_mask_mode->add_item(TTR("Solid Pixels"), EMISSION_MODE_SOLID);
emission_mask_mode->add_item(TTR("Border Pixels"), EMISSION_MODE_BORDER);
emission_mask_mode->add_item(TTR("Directed Border Pixels"), EMISSION_MODE_BORDER_DIRECTED);
+ VBoxContainer *optionsvb = memnew(VBoxContainer);
+ emvb->add_margin_child(TTR("Options"), optionsvb);
+ emission_mask_centered = memnew(CheckBox);
+ emission_mask_centered->set_text(TTR("Centered"));
+ optionsvb->add_child(emission_mask_centered);
emission_colors = memnew(CheckBox);
- emission_colors->set_text(TTR("Capture from Pixel"));
- emvb->add_margin_child(TTR("Emission Colors"), emission_colors);
+ emission_colors->set_text(TTR("Capture Colors from Pixel"));
+ optionsvb->add_child(emission_colors);
toolbar->add_child(emission_mask);
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.h b/editor/plugins/gpu_particles_2d_editor_plugin.h
index aa6d166d85..237a005ab7 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.h
@@ -75,6 +75,7 @@ class GPUParticles2DEditorPlugin : public EditorPlugin {
ConfirmationDialog *emission_mask = nullptr;
OptionButton *emission_mask_mode = nullptr;
+ CheckBox *emission_mask_centered = nullptr;
CheckBox *emission_colors = nullptr;
String source_emission_file;
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 136de43a5a..68d3661d10 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1583,6 +1583,22 @@ void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
}
}
+// This is only active during instant transforms,
+// to capture and wrap mouse events outside the control.
+void Node3DEditorViewport::input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(!_edit.instant);
+ Ref<InputEventMouseMotion> m = p_event;
+
+ if (m.is_valid()) {
+ if (_edit.mode == TRANSFORM_ROTATE) {
+ _edit.mouse_pos = m->get_position(); // rotate should not wrap
+ } else {
+ _edit.mouse_pos += _get_warped_mouse_motion(p_event);
+ }
+ update_transform(_get_key_modifier(m) == Key::SHIFT);
+ }
+}
+
void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (previewing) {
return; //do NONE
@@ -1906,7 +1922,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> m = p_event;
- if (m.is_valid()) {
+ // Instant transforms process mouse motion in input() to handle wrapping.
+ if (m.is_valid() && !_edit.instant) {
_edit.mouse_pos = m->get_position();
if (spatial_editor->get_single_selected_node()) {
@@ -1956,7 +1973,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
String n = _edit.gizmo->get_handle_name(_edit.gizmo_handle, _edit.gizmo_handle_secondary);
set_message(n + ": " + String(v));
- } else if (m->get_button_mask().has_flag(MouseButtonMask::LEFT) || _edit.instant) {
+ } else if (m->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) {
nav_mode = NAVIGATION_ORBIT;
} else if (nav_scheme == NAVIGATION_MODO && m->is_alt_pressed() && m->is_shift_pressed()) {
@@ -1993,7 +2010,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
return;
}
- update_transform(m->get_position(), _get_key_modifier(m) == Key::SHIFT);
+ update_transform(_get_key_modifier(m) == Key::SHIFT);
}
} else if (m->get_button_mask().has_flag(MouseButtonMask::RIGHT) || freelook_active) {
if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) {
@@ -2183,7 +2200,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
_edit.plane = TRANSFORM_VIEW;
spatial_editor->set_local_coords_enabled(false);
}
- update_transform(_edit.mouse_pos, Input::get_singleton()->is_key_pressed(Key::SHIFT));
+ update_transform(Input::get_singleton()->is_key_pressed(Key::SHIFT));
set_message(new_message, 2);
accept_event();
return;
@@ -4515,9 +4532,11 @@ void Node3DEditorViewport::begin_transform(TransformMode p_mode, bool instant) {
_edit.instant = instant;
_edit.snap = spatial_editor->is_snap_enabled();
update_transform_gizmo_view();
+ set_process_input(instant);
}
}
+// Apply the current transform operation.
void Node3DEditorViewport::commit_transform() {
ERR_FAIL_COND(_edit.mode == TRANSFORM_NONE);
static const char *_transform_name[4] = {
@@ -4552,9 +4571,10 @@ void Node3DEditorViewport::commit_transform() {
set_message("");
}
-void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) {
- Vector3 ray_pos = _get_ray_pos(p_mousepos);
- Vector3 ray = _get_ray(p_mousepos);
+// Update the current transform operation in response to an input.
+void Node3DEditorViewport::update_transform(bool p_shift) {
+ Vector3 ray_pos = _get_ray_pos(_edit.mouse_pos);
+ Vector3 ray = _get_ray(_edit.mouse_pos);
double snap = EDITOR_GET("interface/inspector/default_float_step");
int snap_step_decimals = Math::range_step_decimals(snap);
@@ -4894,12 +4914,14 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) {
}
}
+// Perform cleanup after a transform operation is committed or cancelled.
void Node3DEditorViewport::finish_transform() {
spatial_editor->set_local_coords_enabled(_edit.original_local);
_edit.mode = TRANSFORM_NONE;
_edit.instant = false;
spatial_editor->update_transform_gizmo();
surface->queue_redraw();
+ set_process_input(false);
}
// Register a shortcut and also add it as an input action with the same events.
@@ -4907,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/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 28803e67cb..79674bdd64 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -401,6 +401,7 @@ private:
void _surface_focus_enter();
void _surface_focus_exit();
+ void input(const Ref<InputEvent> &p_event) override;
void _sinput(const Ref<InputEvent> &p_event);
void _update_freelook(real_t delta);
Node3DEditor *spatial_editor = nullptr;
@@ -444,7 +445,7 @@ private:
void begin_transform(TransformMode p_mode, bool instant);
void commit_transform();
- void update_transform(Point2 p_mousepos, bool p_shift);
+ void update_transform(bool p_shift);
void finish_transform();
void register_shortcut_action(const String &p_path, const String &p_name, Key p_keycode, bool p_physical = false);
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 1d449d79a6..78ba26503d 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -846,13 +846,14 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
_update_selected_editor_menu();
}
- _update_history_arrows();
-
- _update_script_names();
- _update_members_overview_visibility();
- _update_help_overview_visibility();
- _save_layout();
- _update_find_replace_bar();
+ if (script_close_queue.is_empty()) {
+ _update_history_arrows();
+ _update_script_names();
+ _update_members_overview_visibility();
+ _update_help_overview_visibility();
+ _save_layout();
+ _update_find_replace_bar();
+ }
}
void ScriptEditor::_close_current_tab(bool p_save) {
@@ -2434,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()) {
@@ -3911,8 +3924,8 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
_update_recent_scripts();
file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL | Key::S), FILE_SAVE);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As...")), FILE_SAVE_AS);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KeyModifierMask::CMD_OR_CTRL | Key::S), FILE_SAVE);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::S), FILE_SAVE_AS);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KeyModifierMask::SHIFT | KeyModifierMask::ALT | Key::S), FILE_SAVE_ALL);
ED_SHORTCUT_OVERRIDE("script_editor/save_all", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::S);
file_menu->get_popup()->add_separator();
@@ -4206,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..1dfabf70fd 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/gui/editor_toaster.h"
#include "scene/gui/rich_text_label.h"
#include "scene/gui/split_container.h"
@@ -282,6 +283,22 @@ void ScriptTextEditor::_error_clicked(Variant p_line) {
if (p_line.get_type() == Variant::INT) {
code_editor->get_text_editor()->remove_secondary_carets();
code_editor->get_text_editor()->set_caret_line(p_line.operator int64_t());
+ } else if (p_line.get_type() == Variant::DICTIONARY) {
+ Dictionary meta = p_line.operator Dictionary();
+ const String path = meta["path"].operator String();
+ const int line = meta["line"].operator int64_t();
+ const int column = meta["column"].operator int64_t();
+ if (path.is_empty()) {
+ code_editor->get_text_editor()->remove_secondary_carets();
+ code_editor->get_text_editor()->set_caret_line(line);
+ } else {
+ Ref<Resource> scr = ResourceLoader::load(path);
+ if (!scr.is_valid()) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not load file at:") + "\n\n" + path, TTR("Error!"));
+ } else {
+ ScriptEditor::get_singleton()->edit(scr, line, column);
+ }
+ }
}
}
@@ -458,9 +475,17 @@ void ScriptTextEditor::_validate_script() {
warnings.clear();
errors.clear();
+ depended_errors.clear();
safe_lines.clear();
if (!script->get_language()->validate(text, script->get_path(), &fnc, &errors, &warnings, &safe_lines)) {
+ for (List<ScriptLanguage::ScriptError>::Element *E = errors.front(); E; E = E->next()) {
+ if (E->get().path.is_empty() || E->get().path != script->get_path()) {
+ depended_errors[E->get().path].push_back(E->get());
+ E->erase();
+ }
+ }
+
// TRANSLATORS: Script error pointing to a line and column number.
String error_text = vformat(TTR("Error at (%d, %d):"), errors[0].line, errors[0].column) + " " + errors[0].message;
code_editor->set_error(error_text);
@@ -560,6 +585,10 @@ void ScriptTextEditor::_update_errors() {
errors_panel->clear();
errors_panel->push_table(2);
for (const ScriptLanguage::ScriptError &err : errors) {
+ Dictionary click_meta;
+ click_meta["line"] = err.line;
+ click_meta["column"] = err.column;
+
errors_panel->push_cell();
errors_panel->push_meta(err.line - 1);
errors_panel->push_color(warnings_panel->get_theme_color(SNAME("error_color"), SNAME("Editor")));
@@ -575,6 +604,41 @@ void ScriptTextEditor::_update_errors() {
}
errors_panel->pop(); // Table
+ for (const KeyValue<String, List<ScriptLanguage::ScriptError>> &KV : depended_errors) {
+ Dictionary click_meta;
+ click_meta["path"] = KV.key;
+ click_meta["line"] = 1;
+
+ errors_panel->add_newline();
+ errors_panel->add_newline();
+ errors_panel->push_meta(click_meta);
+ errors_panel->add_text(vformat(R"(%s:)", KV.key));
+ errors_panel->pop(); // Meta goto.
+ errors_panel->add_newline();
+
+ errors_panel->push_indent(1);
+ errors_panel->push_table(2);
+ String filename = KV.key.get_file();
+ for (const ScriptLanguage::ScriptError &err : KV.value) {
+ click_meta["line"] = err.line;
+ click_meta["column"] = err.column;
+
+ errors_panel->push_cell();
+ errors_panel->push_meta(click_meta);
+ errors_panel->push_color(errors_panel->get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ errors_panel->add_text(TTR("Line") + " " + itos(err.line) + ":");
+ errors_panel->pop(); // Color.
+ errors_panel->pop(); // Meta goto.
+ errors_panel->pop(); // Cell.
+
+ errors_panel->push_cell();
+ errors_panel->add_text(err.message);
+ errors_panel->pop(); // Cell.
+ }
+ errors_panel->pop(); // Table
+ errors_panel->pop(); // Indent.
+ }
+
CodeEdit *te = code_editor->get_text_editor();
bool highlight_safe = EDITOR_GET("text_editor/appearance/gutters/highlight_type_safe_lines");
bool last_is_safe = false;
@@ -811,6 +875,8 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
}
ScriptLanguage::LookupResult result;
+ String code_text = code_editor->get_text_editor()->get_text_with_cursor_char(p_row, p_column);
+ Error lc_error = script->get_language()->lookup_code(code_text, p_symbol, script->get_path(), base, result);
if (ScriptServer::is_global_class(p_symbol)) {
EditorNode::get_singleton()->load_resource(ScriptServer::get_global_class_path(p_symbol));
} else if (p_symbol.is_resource_file()) {
@@ -823,7 +889,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
EditorNode::get_singleton()->load_resource(p_symbol);
}
- } else if (script->get_language()->lookup_code(code_editor->get_text_editor()->get_text_for_symbol_lookup(), p_symbol, script->get_path(), base, result) == OK) {
+ } else if (lc_error == OK) {
_goto_line(p_row);
switch (result.type) {
@@ -944,7 +1010,10 @@ void ScriptTextEditor::_validate_symbol(const String &p_symbol) {
}
ScriptLanguage::LookupResult result;
- if (ScriptServer::is_global_class(p_symbol) || p_symbol.is_resource_file() || script->get_language()->lookup_code(code_editor->get_text_editor()->get_text_for_symbol_lookup(), p_symbol, script->get_path(), base, result) == OK || (ProjectSettings::get_singleton()->has_autoload(p_symbol) && ProjectSettings::get_singleton()->get_autoload(p_symbol).is_singleton)) {
+ String lc_text = code_editor->get_text_editor()->get_text_for_symbol_lookup();
+ Error lc_error = script->get_language()->lookup_code(lc_text, p_symbol, script->get_path(), base, result);
+ bool is_singleton = ProjectSettings::get_singleton()->has_autoload(p_symbol) && ProjectSettings::get_singleton()->get_autoload(p_symbol).is_singleton;
+ if (ScriptServer::is_global_class(p_symbol) || p_symbol.is_resource_file() || lc_error == OK || is_singleton) {
text_edit->set_symbol_lookup_word_as_valid(true);
} else if (p_symbol.is_relative_path()) {
String path = _get_absolute_path(p_symbol);
@@ -1691,10 +1760,14 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
return;
}
+ if (!ClassDB::is_parent_class(script->get_instance_base_type(), "Node")) {
+ EditorToaster::get_singleton()->popup_str(vformat(TTR("Can't drop nodes because script '%s' does not inherit Node."), get_name()), EditorToaster::SEVERITY_WARNING);
+ return;
+ }
+
Node *sn = _find_script_node(scene_root, scene_root, script);
if (!sn) {
- EditorNode::get_singleton()->show_warning(vformat(TTR("Can't drop nodes because script '%s' is not used in this scene."), get_name()));
- return;
+ sn = scene_root;
}
Array nodes = d["nodes"];
@@ -1726,9 +1799,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 {
@@ -1841,7 +1922,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
bool open_docs = false;
bool goto_definition = false;
- if (word_at_pos.is_resource_file()) {
+ if (ScriptServer::is_global_class(word_at_pos) || word_at_pos.is_resource_file()) {
open_docs = true;
} else {
Node *base = get_tree()->get_edited_scene_root();
@@ -2285,7 +2366,7 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/indent", TTR("Indent"), Key::NONE);
ED_SHORTCUT("script_text_editor/unindent", TTR("Unindent"), KeyModifierMask::SHIFT | Key::TAB);
- ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KeyModifierMask::CMD_OR_CTRL | Key::K);
+ ED_SHORTCUT_ARRAY("script_text_editor/toggle_comment", TTR("Toggle Comment"), { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::K), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::SLASH) });
ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KeyModifierMask::ALT | Key::F);
ED_SHORTCUT_OVERRIDE("script_text_editor/toggle_fold_line", "macos", KeyModifierMask::CTRL | KeyModifierMask::META | Key::F);
ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), Key::NONE);
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 808a6417e4..d275013b91 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -68,6 +68,7 @@ class ScriptTextEditor : public ScriptEditorBase {
Vector<String> functions;
List<ScriptLanguage::Warning> warnings;
List<ScriptLanguage::ScriptError> errors;
+ HashMap<String, List<ScriptLanguage::ScriptError>> depended_errors;
HashSet<int> safe_lines;
List<Connection> missing_connections;
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..7b35351457 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);
@@ -948,13 +949,16 @@ void SpriteFramesEditor::_animation_name_edited() {
String name = new_name;
int counter = 0;
while (frames->has_animation(name)) {
+ if (name == String(edited_anim)) {
+ edited->set_text(0, name); // The name didn't change, just updated the column text to name.
+ return;
+ }
counter++;
name = new_name + "_" + itos(counter);
}
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Rename Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
- _rename_node_animation(undo_redo, false, edited_anim, "", "");
undo_redo->add_do_method(frames.ptr(), "rename_animation", edited_anim, name);
undo_redo->add_undo_method(frames.ptr(), "rename_animation", name, edited_anim);
_rename_node_animation(undo_redo, false, edited_anim, name, name);
@@ -1208,30 +1212,29 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) {
bool searching = anim_search_box->get_text().size();
String searched_string = searching ? anim_search_box->get_text().to_lower() : String();
+ TreeItem *selected = nullptr;
for (const StringName &E : anim_names) {
String name = E;
-
if (searching && name.to_lower().find(searched_string) < 0) {
continue;
}
-
TreeItem *it = animations->create_item(anim_root);
-
it->set_metadata(0, name);
-
it->set_text(0, name);
it->set_editable(0, true);
-
if (animated_sprite) {
if (name == String(animated_sprite->call("get_autoplay"))) {
it->set_icon(0, autoplay_icon);
}
}
-
if (E == edited_anim) {
it->select(0);
+ selected = it;
}
}
+ if (selected) {
+ animations->scroll_to_item(selected);
+ }
}
if (animated_sprite) {
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/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp
index f4a6c4af2d..fb8cb57d4d 100644
--- a/editor/plugins/theme_editor_preview.cpp
+++ b/editor/plugins/theme_editor_preview.cpp
@@ -321,7 +321,7 @@ DefaultThemeEditorPreview::DefaultThemeEditorPreview() {
first_vb->add_child(bt);
Button *tb = memnew(Button);
tb->set_flat(true);
- tb->set_text("Button");
+ tb->set_text("Flat Button");
first_vb->add_child(tb);
CheckButton *cb = memnew(CheckButton);
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..0fbce2a677 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();
}
@@ -685,6 +685,14 @@ void GenericTilePolygonEditor::_store_snap_options() {
EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "tile_snap_subdiv", snap_subdivision->get_value());
}
+void GenericTilePolygonEditor::_toggle_expand(bool p_expand) {
+ if (p_expand) {
+ TileSetEditor::get_singleton()->add_expanded_editor(this);
+ } else {
+ TileSetEditor::get_singleton()->remove_expanded_editor();
+ }
+}
+
void GenericTilePolygonEditor::set_use_undo_redo(bool p_use_undo_redo) {
use_undo_redo = p_use_undo_redo;
}
@@ -793,8 +801,13 @@ void GenericTilePolygonEditor::set_multiple_polygon_mode(bool p_multiple_polygon
void GenericTilePolygonEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_ENTER_TREE: {
+ if (!get_meta("reparented", false)) {
+ button_expand->set_pressed_no_signal(false);
+ }
+ } break;
case NOTIFICATION_THEME_CHANGED: {
+ button_expand->set_icon(get_theme_icon(SNAME("DistractionFree"), SNAME("EditorIcons")));
button_create->set_icon(get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons")));
button_edit->set_icon(get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
button_delete->set_icon(get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
@@ -831,6 +844,16 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
tools_button_group.instantiate();
+ button_expand = memnew(Button);
+ button_expand->set_flat(true);
+ button_expand->set_toggle_mode(true);
+ button_expand->set_pressed(false);
+ button_expand->set_tooltip_text(TTR("Expand editor"));
+ button_expand->connect("toggled", callable_mp(this, &GenericTilePolygonEditor::_toggle_expand));
+ toolbar->add_child(button_expand);
+
+ toolbar->add_child(memnew(VSeparator));
+
button_create = memnew(Button);
button_create->set_flat(true);
button_create->set_toggle_mode(true);
@@ -885,7 +908,7 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
snap_subdivision->set_max(99);
Control *root = memnew(Control);
- root->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ root->set_v_size_flags(Control::SIZE_EXPAND_FILL);
root->set_custom_minimum_size(Size2(0, 200 * EDSCALE));
root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
add_child(root);
diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h
index b3ecdb8cfb..4bba5bb467 100644
--- a/editor/plugins/tiles/tile_data_editors.h
+++ b/editor/plugins/tiles/tile_data_editors.h
@@ -36,10 +36,11 @@
#include "editor/editor_properties.h"
#include "scene/2d/tile_map.h"
#include "scene/gui/box_container.h"
+#include "scene/gui/panel_container.h"
+class Label;
class MenuButton;
class SpinBox;
-class Label;
class EditorUndoRedoManager;
class TileDataEditor : public VBoxContainer {
@@ -117,6 +118,7 @@ private:
HBoxContainer *toolbar = nullptr;
Ref<ButtonGroup> tools_button_group;
+ Button *button_expand = nullptr;
Button *button_create = nullptr;
Button *button_edit = nullptr;
Button *button_delete = nullptr;
@@ -165,6 +167,7 @@ private:
void _base_control_gui_input(Ref<InputEvent> p_event);
void _set_snap_option(int p_index);
void _store_snap_options();
+ void _toggle_expand(bool p_expand);
void _snap_to_tile_shape(Point2 &r_point, float &r_current_snapped_dist, float p_snap_dist);
void _snap_point(Point2 &r_point);
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index aac7fb3b84..8b58a45ea5 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -109,8 +109,8 @@ void TileMapEditorTilesPlugin::_update_toolbar() {
}
}
-Vector<TileMapEditorPlugin::TabData> TileMapEditorTilesPlugin::get_tabs() const {
- Vector<TileMapEditorPlugin::TabData> tabs;
+Vector<TileMapSubEditorPlugin::TabData> TileMapEditorTilesPlugin::get_tabs() const {
+ Vector<TileMapSubEditorPlugin::TabData> tabs;
tabs.push_back({ toolbar, tiles_bottom_panel });
tabs.push_back({ toolbar, patterns_bottom_panel });
return tabs;
@@ -147,7 +147,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
old_source = -1;
}
- List<int> source_ids = TilesEditorPlugin::get_singleton()->get_sorted_sources(tile_set);
+ List<int> source_ids = TilesEditorUtils::get_singleton()->get_sorted_sources(tile_set);
for (const int &source_id : source_ids) {
TileSetSource *source = *tile_set->get_source(source_id);
@@ -156,7 +156,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
// Common to all type of sources.
if (!source->get_name().is_empty()) {
- item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id);
+ item_text = source->get_name();
}
// Atlas source.
@@ -165,7 +165,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
texture = atlas_source->get_texture();
if (item_text.is_empty()) {
if (texture.is_valid()) {
- item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id);
+ item_text = texture->get_path().get_file();
} else {
item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
}
@@ -209,7 +209,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
}
// Synchronize the lists.
- TilesEditorPlugin::get_singleton()->set_sources_lists_current(sources_list->get_current());
+ TilesEditorUtils::get_singleton()->set_sources_lists_current(sources_list->get_current());
}
void TileMapEditorTilesPlugin::_update_source_display() {
@@ -322,7 +322,7 @@ void TileMapEditorTilesPlugin::_update_patterns_list() {
int id = patterns_item_list->add_item("");
patterns_item_list->set_item_metadata(id, tile_set->get_pattern(i));
patterns_item_list->set_item_tooltip(id, vformat(TTR("Index: %d"), i));
- TilesEditorPlugin::get_singleton()->queue_pattern_preview(tile_set, tile_set->get_pattern(i), callable_mp(this, &TileMapEditorTilesPlugin::_pattern_preview_done));
+ TilesEditorUtils::get_singleton()->queue_pattern_preview(tile_set, tile_set->get_pattern(i), callable_mp(this, &TileMapEditorTilesPlugin::_pattern_preview_done));
}
// Update the label visibility.
@@ -354,7 +354,7 @@ void TileMapEditorTilesPlugin::_update_atlas_view() {
ERR_FAIL_COND(!atlas_source);
tile_atlas_view->set_atlas_source(*tile_map->get_tileset(), atlas_source, source_id);
- TilesEditorPlugin::get_singleton()->synchronize_atlas_view(tile_atlas_view);
+ TilesEditorUtils::get_singleton()->synchronize_atlas_view(tile_atlas_view);
tile_atlas_control->queue_redraw();
}
@@ -766,9 +766,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
for (int y = rect.position.y; y < rect.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
- Transform2D tile_xform;
- tile_xform.set_origin(tile_map->map_to_local(coords));
- tile_xform.set_scale(tile_shape_size);
+ Transform2D tile_xform(0, tile_shape_size, 0, tile_map->map_to_local(coords));
tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0), false);
}
}
@@ -784,6 +782,8 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
to_draw.insert(coords);
}
+ Transform2D tile_xform(0, tile_shape_size, 0, tile_map->map_to_local(coords));
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.2), true);
}
}
tile_map->draw_cells_outline(p_overlay, to_draw, Color(1.0, 1.0, 1.0), xform);
@@ -1386,7 +1386,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
for (int i = 0; i < sources_list->get_item_count(); i++) {
if (int(sources_list->get_item_metadata(i)) == picked_source) {
sources_list->set_current(i);
- TilesEditorPlugin::get_singleton()->set_sources_lists_current(i);
+ TilesEditorUtils::get_singleton()->set_sources_lists_current(i);
break;
}
}
@@ -1720,7 +1720,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
if (frame > 0) {
color.a *= 0.3;
}
- TilesEditorPlugin::draw_selection_rect(tile_atlas_control, atlas->get_tile_texture_region(E.get_atlas_coords(), frame), color);
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, atlas->get_tile_texture_region(E.get_atlas_coords(), frame), color);
}
}
}
@@ -1729,7 +1729,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) {
for (int frame = 0; frame < atlas->get_tile_animation_frames_count(hovered_tile.get_atlas_coords()); frame++) {
Color color = Color(1.0, 0.8, 0.0, frame == 0 ? 0.6 : 0.3);
- TilesEditorPlugin::draw_selection_rect(tile_atlas_control, atlas->get_tile_texture_region(hovered_tile.get_atlas_coords(), frame), color);
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, atlas->get_tile_texture_region(hovered_tile.get_atlas_coords(), frame), color);
}
}
@@ -1751,7 +1751,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
}
}
for (const Vector2i &E : to_draw) {
- TilesEditorPlugin::draw_selection_rect(tile_atlas_control, atlas->get_tile_texture_region(E));
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, atlas->get_tile_texture_region(E));
}
}
}
@@ -1900,7 +1900,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() {
if (E.source_id == source_id && E.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && E.alternative_tile > 0) {
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E.get_atlas_coords(), E.alternative_tile);
if (rect != Rect2i()) {
- TilesEditorPlugin::draw_selection_rect(alternative_tiles_control, rect);
+ TilesEditorUtils::draw_selection_rect(alternative_tiles_control, rect);
}
}
}
@@ -1909,7 +1909,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() {
if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile > 0) {
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile);
if (rect != Rect2i()) {
- TilesEditorPlugin::draw_selection_rect(alternative_tiles_control, rect, Color(1.0, 0.8, 0.0, 0.5));
+ TilesEditorUtils::draw_selection_rect(alternative_tiles_control, rect, Color(1.0, 0.8, 0.0, 0.5));
}
}
}
@@ -2031,10 +2031,10 @@ void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer
}
void TileMapEditorTilesPlugin::_set_source_sort(int p_sort) {
- for (int i = 0; i != TilesEditorPlugin::SOURCE_SORT_MAX; i++) {
+ for (int i = 0; i != TilesEditorUtils::SOURCE_SORT_MAX; i++) {
source_sort_button->get_popup()->set_item_checked(i, (i == (int)p_sort));
}
- TilesEditorPlugin::get_singleton()->set_sorting_option(p_sort);
+ TilesEditorUtils::get_singleton()->set_sorting_option(p_sort);
_update_tile_set_sources_list();
EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "tile_source_sort", p_sort);
}
@@ -2217,11 +2217,11 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
PopupMenu *p = source_sort_button->get_popup();
p->connect("id_pressed", callable_mp(this, &TileMapEditorTilesPlugin::_set_source_sort));
- p->add_radio_check_item(TTR("Sort by ID (Ascending)"), TilesEditorPlugin::SOURCE_SORT_ID);
- p->add_radio_check_item(TTR("Sort by ID (Descending)"), TilesEditorPlugin::SOURCE_SORT_ID_REVERSE);
- p->add_radio_check_item(TTR("Sort by Name (Ascending)"), TilesEditorPlugin::SOURCE_SORT_NAME);
- p->add_radio_check_item(TTR("Sort by Name (Descending)"), TilesEditorPlugin::SOURCE_SORT_NAME_REVERSE);
- p->set_item_checked(TilesEditorPlugin::SOURCE_SORT_ID, true);
+ p->add_radio_check_item(TTR("Sort by ID (Ascending)"), TilesEditorUtils::SOURCE_SORT_ID);
+ p->add_radio_check_item(TTR("Sort by ID (Descending)"), TilesEditorUtils::SOURCE_SORT_ID_REVERSE);
+ p->add_radio_check_item(TTR("Sort by Name (Ascending)"), TilesEditorUtils::SOURCE_SORT_NAME);
+ p->add_radio_check_item(TTR("Sort by Name (Descending)"), TilesEditorUtils::SOURCE_SORT_NAME_REVERSE);
+ p->set_item_checked(TilesEditorUtils::SOURCE_SORT_ID, true);
sources_bottom_actions->add_child(source_sort_button);
sources_list = memnew(ItemList);
@@ -2233,8 +2233,9 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1));
sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_source_display).unbind(1));
- sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
- sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list).bind(sources_list, source_sort_button));
+ sources_list->connect("item_selected", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::set_sources_lists_current));
+ sources_list->connect("item_activated", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::display_tile_set_editor_panel).unbind(1));
+ sources_list->connect("visibility_changed", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::synchronize_sources_list).bind(sources_list, source_sort_button));
sources_list->add_user_signal(MethodInfo("sort_request"));
sources_list->connect("sort_request", callable_mp(this, &TileMapEditorTilesPlugin::_update_tile_set_sources_list));
split_container_left_side->add_child(sources_list);
@@ -2246,7 +2247,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
tile_atlas_view->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tile_atlas_view->set_texture_grid_visible(false);
tile_atlas_view->set_tile_shape_grid_visible(false);
- tile_atlas_view->connect("transform_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_atlas_view_transform));
+ tile_atlas_view->connect("transform_changed", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::set_atlas_view_transform));
atlas_sources_split_container->add_child(tile_atlas_view);
tile_atlas_control = memnew(Control);
@@ -2353,8 +2354,8 @@ void TileMapEditorTerrainsPlugin::_update_toolbar() {
}
}
-Vector<TileMapEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() const {
- Vector<TileMapEditorPlugin::TabData> tabs;
+Vector<TileMapSubEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() const {
+ Vector<TileMapSubEditorPlugin::TabData> tabs;
tabs.push_back({ toolbar, main_vbox_container });
return tabs;
}
@@ -3549,7 +3550,7 @@ void TileMapEditor::_update_bottom_panel() {
// Update the visibility of controls.
missing_tileset_label->set_visible(!tile_set.is_valid());
- for (TileMapEditorPlugin::TabData &tab_data : tabs_data) {
+ for (TileMapSubEditorPlugin::TabData &tab_data : tabs_data) {
tab_data.panel->hide();
}
if (tile_set.is_valid()) {
@@ -3639,14 +3640,14 @@ void TileMapEditor::_tab_changed(int p_tab_id) {
tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
// Update toolbar.
- for (TileMapEditorPlugin::TabData &tab_data : tabs_data) {
+ for (TileMapSubEditorPlugin::TabData &tab_data : tabs_data) {
tab_data.toolbar->hide();
}
tabs_data[p_tab_id].toolbar->show();
// Update visible panel.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- for (TileMapEditorPlugin::TabData &tab_data : tabs_data) {
+ for (TileMapSubEditorPlugin::TabData &tab_data : tabs_data) {
tab_data.panel->hide();
}
if (tile_map && tile_map->get_tileset().is_valid()) {
@@ -4006,7 +4007,7 @@ TileMapEditor::TileMapEditor() {
tabs_bar = memnew(TabBar);
tabs_bar->set_clip_tabs(false);
for (int plugin_index = 0; plugin_index < tile_map_editor_plugins.size(); plugin_index++) {
- Vector<TileMapEditorPlugin::TabData> tabs_vector = tile_map_editor_plugins[plugin_index]->get_tabs();
+ Vector<TileMapSubEditorPlugin::TabData> tabs_vector = tile_map_editor_plugins[plugin_index]->get_tabs();
for (int tab_index = 0; tab_index < tabs_vector.size(); tab_index++) {
tabs_bar->add_tab(tabs_vector[tab_index].panel->get_name());
tabs_data.push_back(tabs_vector[tab_index]);
@@ -4024,7 +4025,7 @@ TileMapEditor::TileMapEditor() {
tile_map_toolbar->add_child(tabs_bar);
// Tabs toolbars.
- for (TileMapEditorPlugin::TabData &tab_data : tabs_data) {
+ for (TileMapSubEditorPlugin::TabData &tab_data : tabs_data) {
tab_data.toolbar->hide();
if (!tab_data.toolbar->get_parent()) {
tile_map_toolbar->add_child(tab_data.toolbar);
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index 1cab1d1500..eb8c3937a0 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -48,7 +48,7 @@
#include "scene/gui/tab_bar.h"
#include "scene/gui/tree.h"
-class TileMapEditorPlugin : public Object {
+class TileMapSubEditorPlugin : public Object {
public:
struct TabData {
Control *toolbar = nullptr;
@@ -65,8 +65,8 @@ public:
virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer){};
};
-class TileMapEditorTilesPlugin : public TileMapEditorPlugin {
- GDCLASS(TileMapEditorTilesPlugin, TileMapEditorPlugin);
+class TileMapEditorTilesPlugin : public TileMapSubEditorPlugin {
+ GDCLASS(TileMapEditorTilesPlugin, TileMapSubEditorPlugin);
private:
ObjectID tile_map_id;
@@ -219,8 +219,8 @@ public:
~TileMapEditorTilesPlugin();
};
-class TileMapEditorTerrainsPlugin : public TileMapEditorPlugin {
- GDCLASS(TileMapEditorTerrainsPlugin, TileMapEditorPlugin);
+class TileMapEditorTerrainsPlugin : public TileMapSubEditorPlugin {
+ GDCLASS(TileMapEditorTerrainsPlugin, TileMapSubEditorPlugin);
private:
ObjectID tile_map_id;
@@ -321,7 +321,7 @@ private:
int tile_map_layer = -1;
// Vector to keep plugins.
- Vector<TileMapEditorPlugin *> tile_map_editor_plugins;
+ Vector<TileMapSubEditorPlugin *> tile_map_editor_plugins;
// Toolbar.
HFlowContainer *tile_map_toolbar = nullptr;
@@ -339,8 +339,8 @@ private:
// Bottom panel.
Label *missing_tileset_label = nullptr;
TabBar *tabs_bar = nullptr;
- LocalVector<TileMapEditorPlugin::TabData> tabs_data;
- LocalVector<TileMapEditorPlugin *> tabs_plugins;
+ LocalVector<TileMapSubEditorPlugin::TabData> tabs_data;
+ LocalVector<TileMapSubEditorPlugin *> tabs_plugins;
void _update_bottom_panel();
// TileMap.
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 6ec45b1f95..c98d9086d1 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/plugins/tiles/tile_set_editor.h"
#include "editor/progress_dialog.h"
#include "scene/gui/box_container.h"
@@ -246,6 +247,12 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
}
emit_signal(SNAME("changed"), "animation_speed");
return true;
+ } else if (p_name == "animation_mode") {
+ for (TileSelection tile : tiles) {
+ tile_set_atlas_source->set_tile_animation_mode(tile.tile, VariantCaster<TileSetAtlasSource::TileAnimationMode>::cast(p_value));
+ }
+ emit_signal(SNAME("changed"), "animation_mode");
+ return true;
} else if (p_name == "animation_frames_count") {
for (TileSelection tile : tiles) {
int frame_count = p_value;
@@ -349,6 +356,9 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na
} else if (p_name == "animation_speed") {
r_ret = tile_set_atlas_source->get_tile_animation_speed(coords);
return true;
+ } else if (p_name == "animation_mode") {
+ r_ret = tile_set_atlas_source->get_tile_animation_mode(coords);
+ return true;
} else if (p_name == "animation_frames_count") {
r_ret = tile_set_atlas_source->get_tile_animation_frames_count(coords);
return true;
@@ -417,6 +427,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro
p_list->push_back(PropertyInfo(Variant::INT, PNAME("animation_columns")));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, PNAME("animation_separation")));
p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("animation_speed")));
+ p_list->push_back(PropertyInfo(Variant::INT, PNAME("animation_mode"), PROPERTY_HINT_ENUM, "Default,Random Start Times"));
p_list->push_back(PropertyInfo(Variant::INT, PNAME("animation_frames_count"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Frames,animation_frame_"));
// Not optimal, but returns value for the first tile. This is similar to what MultiNodeEdit does.
if (tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile) == 1) {
@@ -720,6 +731,8 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
tile_data_editors["probability"] = tile_data_probability_editor;
}
+ Color disabled_color = get_theme_color("disabled_font_color", "Editor");
+
// --- Physics ---
ADD_TILE_DATA_EDITOR_GROUP(TTR("Physics"));
for (int i = 0; i < tile_set->get_physics_layers_count(); i++) {
@@ -738,6 +751,16 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
tile_data_editors.erase(vformat("physics_layer_%d", i));
}
+ if (tile_set->get_physics_layers_count() == 0) {
+ item = tile_data_editors_tree->create_item(group);
+ item->set_icon(0, get_theme_icon("Info", "EditorIcons"));
+ item->set_icon_modulate(0, disabled_color);
+ item->set_text(0, TTR("No physics layers"));
+ item->set_tooltip_text(0, TTR("Create and customize physics layers in the inspector of the TileSet resource."));
+ item->set_selectable(0, false);
+ item->set_custom_color(0, disabled_color);
+ }
+
// --- Navigation ---
ADD_TILE_DATA_EDITOR_GROUP(TTR("Navigation"));
for (int i = 0; i < tile_set->get_navigation_layers_count(); i++) {
@@ -756,6 +779,16 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
tile_data_editors.erase(vformat("navigation_layer_%d", i));
}
+ if (tile_set->get_navigation_layers_count() == 0) {
+ item = tile_data_editors_tree->create_item(group);
+ item->set_icon(0, get_theme_icon("Info", "EditorIcons"));
+ item->set_icon_modulate(0, disabled_color);
+ item->set_text(0, TTR("No navigation layers"));
+ item->set_tooltip_text(0, TTR("Create and customize navigation layers in the inspector of the TileSet resource."));
+ item->set_selectable(0, false);
+ item->set_custom_color(0, disabled_color);
+ }
+
// --- Custom Data ---
ADD_TILE_DATA_EDITOR_GROUP(TTR("Custom Data"));
for (int i = 0; i < tile_set->get_custom_data_layers_count(); i++) {
@@ -789,6 +822,16 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
tile_data_editors.erase(vformat("custom_data_%d", i));
}
+ if (tile_set->get_custom_data_layers_count() == 0) {
+ item = tile_data_editors_tree->create_item(group);
+ item->set_icon(0, get_theme_icon("Info", "EditorIcons"));
+ item->set_icon_modulate(0, disabled_color);
+ item->set_text(0, TTR("No custom data layers"));
+ item->set_tooltip_text(0, TTR("Create and customize custom data layers in the inspector of the TileSet resource."));
+ item->set_selectable(0, false);
+ item->set_custom_color(0, disabled_color);
+ }
+
#undef ADD_TILE_DATA_EDITOR_GROUP
#undef ADD_TILE_DATA_EDITOR
@@ -924,6 +967,8 @@ void TileSetAtlasSourceEditor::_update_atlas_view() {
if (tile_set.is_null()) {
return;
+ } else {
+ tile_create_help->set_visible(tools_button_group->get_pressed_button() == tool_setup_atlas_source_button);
}
Vector2i pos;
@@ -969,7 +1014,7 @@ void TileSetAtlasSourceEditor::_update_atlas_view() {
tile_atlas_view->queue_redraw();
// Synchronize atlas view.
- TilesEditorPlugin::get_singleton()->synchronize_atlas_view(tile_atlas_view);
+ TilesEditorUtils::get_singleton()->synchronize_atlas_view(tile_atlas_view);
}
void TileSetAtlasSourceEditor::_update_toolbar() {
@@ -1099,24 +1144,26 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
}
} else if (drag_type >= DRAG_TYPE_RESIZE_TOP_LEFT && drag_type <= DRAG_TYPE_RESIZE_LEFT) {
// Resizing a tile.
- new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(-1, -1)).min(grid_size);
-
Rect2i old_rect = Rect2i(drag_current_tile, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile));
Rect2i new_rect = old_rect;
if (drag_type == DRAG_TYPE_RESIZE_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT) {
+ new_base_tiles_coords = _get_drag_offset_tile_coords(Vector2i(-1, 0));
new_rect.position.x = MIN(new_base_tiles_coords.x + 1, old_rect.get_end().x - 1);
new_rect.size.x = old_rect.get_end().x - new_rect.position.x;
}
if (drag_type == DRAG_TYPE_RESIZE_TOP || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT) {
+ new_base_tiles_coords = _get_drag_offset_tile_coords(Vector2i(0, -1));
new_rect.position.y = MIN(new_base_tiles_coords.y + 1, old_rect.get_end().y - 1);
new_rect.size.y = old_rect.get_end().y - new_rect.position.y;
}
if (drag_type == DRAG_TYPE_RESIZE_RIGHT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) {
+ new_base_tiles_coords = _get_drag_offset_tile_coords(Vector2i(1, 0));
new_rect.set_end(Vector2i(MAX(new_base_tiles_coords.x, old_rect.position.x + 1), new_rect.get_end().y));
}
if (drag_type == DRAG_TYPE_RESIZE_BOTTOM || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) {
+ new_base_tiles_coords = _get_drag_offset_tile_coords(Vector2i(0, 1));
new_rect.set_end(Vector2i(new_rect.get_end().x, MAX(new_base_tiles_coords.y, old_rect.position.y + 1)));
}
@@ -1523,66 +1570,6 @@ void TileSetAtlasSourceEditor::_end_dragging() {
// Change mouse accordingly.
}
-Control::CursorShape TileSetAtlasSourceEditor::get_cursor_shape(const Point2 &p_pos) const {
- Control::CursorShape cursor_shape = get_default_cursor_shape();
- if (drag_type == DRAG_TYPE_NONE) {
- if (selection.size() == 1) {
- // Change the cursor depending on the hovered thing.
- TileSelection selected = selection.front()->get();
- if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) {
- Transform2D xform = tile_atlas_control->get_global_transform().affine_inverse() * get_global_transform();
- Vector2 mouse_local_pos = xform.xform(p_pos);
- Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
- Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
- Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
- Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
- const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
- const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
- bool can_grow[4];
- for (int i = 0; i < 4; i++) {
- can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
- can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
- }
- for (int i = 0; i < 4; i++) {
- Vector2 pos = rect.position + rect.size * coords[i];
- if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) {
- cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
- }
- Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
- if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
- cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE;
- }
- }
- }
- }
- } else {
- switch (drag_type) {
- case DRAG_TYPE_RESIZE_TOP_LEFT:
- case DRAG_TYPE_RESIZE_BOTTOM_RIGHT:
- cursor_shape = CURSOR_FDIAGSIZE;
- break;
- case DRAG_TYPE_RESIZE_TOP:
- case DRAG_TYPE_RESIZE_BOTTOM:
- cursor_shape = CURSOR_VSIZE;
- break;
- case DRAG_TYPE_RESIZE_TOP_RIGHT:
- case DRAG_TYPE_RESIZE_BOTTOM_LEFT:
- cursor_shape = CURSOR_BDIAGSIZE;
- break;
- case DRAG_TYPE_RESIZE_LEFT:
- case DRAG_TYPE_RESIZE_RIGHT:
- cursor_shape = CURSOR_HSIZE;
- break;
- case DRAG_TYPE_MOVE_TILE:
- cursor_shape = CURSOR_MOVE;
- break;
- default:
- break;
- }
- }
- return cursor_shape;
-}
-
HashMap<Vector2i, List<const PropertyInfo *>> TileSetAtlasSourceEditor::_group_properties_per_tiles(const List<PropertyInfo> &r_list, const TileSetAtlasSource *p_atlas) {
// Group properties per tile.
HashMap<Vector2i, List<const PropertyInfo *>> per_tile;
@@ -1737,7 +1724,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(selected.tile); frame++) {
Color color = Color(0.0, 1.0, 0.0, frame == 0 ? 1.0 : 0.3);
Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile, frame);
- TilesEditorPlugin::draw_selection_rect(tile_atlas_control, region, color);
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, region, color);
}
}
}
@@ -1779,7 +1766,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
// Draw the tiles to be removed.
for (const Vector2i &E : drag_modified_tiles) {
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(E); frame++) {
- TilesEditorPlugin::draw_selection_rect(tile_atlas_control, tile_set_atlas_source->get_tile_texture_region(E, frame), Color(0.0, 0.0, 0.0));
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, tile_set_atlas_source->get_tile_texture_region(E, frame), Color(0.0, 0.0, 0.0));
}
}
} else if (drag_type == DRAG_TYPE_RECT_SELECT || drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT) {
@@ -1806,7 +1793,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
for (const Vector2i &E : to_paint) {
Vector2i coords = E;
- TilesEditorPlugin::draw_selection_rect(tile_atlas_control, tile_set_atlas_source->get_tile_texture_region(coords), color);
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, tile_set_atlas_source->get_tile_texture_region(coords), color);
}
} else if (drag_type == DRAG_TYPE_CREATE_TILES_USING_RECT) {
// Draw tiles to be created.
@@ -1823,7 +1810,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Vector2i coords = Vector2i(x, y);
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
Vector2i origin = margins + (coords * (tile_size + separation));
- TilesEditorPlugin::draw_selection_rect(tile_atlas_control, Rect2i(origin, tile_size));
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, Rect2i(origin, tile_size));
}
}
}
@@ -1840,7 +1827,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i tile_size = tile_set_atlas_source->get_texture_region_size();
Vector2i origin = margins + (area.position * (tile_size + separation));
- TilesEditorPlugin::draw_selection_rect(tile_atlas_control, Rect2i(origin, area.size * tile_size));
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, Rect2i(origin, area.size * tile_size));
} else {
Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
if (hovered_base_tile_coords.x >= 0 && hovered_base_tile_coords.y >= 0 && hovered_base_tile_coords.x < grid_size.x && hovered_base_tile_coords.y < grid_size.y) {
@@ -1849,7 +1836,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
// Draw existing hovered tile.
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(hovered_tile); frame++) {
Color color = Color(1.0, 0.8, 0.0, frame == 0 ? 0.6 : 0.3);
- TilesEditorPlugin::draw_selection_rect(tile_atlas_control, tile_set_atlas_source->get_tile_texture_region(hovered_tile, frame), color);
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, tile_set_atlas_source->get_tile_texture_region(hovered_tile, frame), color);
}
} else {
// Draw empty tile, only in add/remove tiles mode.
@@ -1858,7 +1845,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i tile_size = tile_set_atlas_source->get_texture_region_size();
Vector2i origin = margins + (hovered_base_tile_coords * (tile_size + separation));
- TilesEditorPlugin::draw_selection_rect(tile_atlas_control, Rect2i(origin, tile_size));
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, Rect2i(origin, tile_size));
}
}
}
@@ -2017,7 +2004,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() {
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(coords, hovered_alternative_tile_coords.z);
if (rect != Rect2i()) {
- TilesEditorPlugin::draw_selection_rect(alternative_tiles_control, rect, Color(1.0, 0.8, 0.0, 0.5));
+ TilesEditorUtils::draw_selection_rect(alternative_tiles_control, rect, Color(1.0, 0.8, 0.0, 0.5));
}
}
@@ -2027,7 +2014,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() {
if (selected.alternative >= 1) {
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(selected.tile, selected.alternative);
if (rect != Rect2i()) {
- TilesEditorPlugin::draw_selection_rect(alternative_tiles_control, rect);
+ TilesEditorUtils::draw_selection_rect(alternative_tiles_control, rect);
}
}
}
@@ -2094,7 +2081,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;
}
@@ -2156,7 +2143,7 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
Ref<TileSetAtlasSource> atlas_source = atlas_source_proxy->get_edited();
ERR_FAIL_COND(!atlas_source.is_valid());
- UndoRedo *internal_undo_redo = undo_redo_man->get_history_for_object(atlas_source.ptr()).undo_redo;
+ UndoRedo *internal_undo_redo = undo_redo_man->get_history_for_object(atlas_source_proxy).undo_redo;
internal_undo_redo->start_force_keep_in_merge_ends();
PackedVector2Array arr;
@@ -2180,7 +2167,7 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
String prefix = vformat("%d:%d/", coords.x, coords.y);
for (PropertyInfo pi : properties) {
if (pi.name.begins_with(prefix)) {
- ADD_UNDO(atlas_source.ptr(), pi.name);
+ ADD_UNDO(atlas_source_proxy, pi.name);
}
}
}
@@ -2191,6 +2178,12 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
#undef ADD_UNDO
}
+Vector2i TileSetAtlasSourceEditor::_get_drag_offset_tile_coords(const Vector2i &p_offset) const {
+ Vector2i half_tile_size = tile_set->get_tile_size() / 2;
+ Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() + half_tile_size * p_offset);
+ return new_base_tiles_coords.max(Vector2i(-1, -1)).min(tile_set_atlas_source->get_atlas_grid_size());
+}
+
void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id) {
ERR_FAIL_COND(!p_tile_set.is_valid());
ERR_FAIL_COND(!p_tile_set_atlas_source);
@@ -2208,7 +2201,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.
@@ -2223,7 +2216,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) {
@@ -2250,6 +2243,7 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
}
void TileSetAtlasSourceEditor::init_source() {
+ tool_setup_atlas_source_button->set_pressed(true);
confirm_auto_create_tiles->popup_centered();
}
@@ -2427,6 +2421,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
set_shortcut_context(this);
set_process_shortcut_input(true);
set_process_internal(true);
+ TileSetEditor::get_singleton()->register_split(this);
// Middle panel.
VBoxContainer *middle_vbox_container = memnew(VBoxContainer);
@@ -2488,7 +2483,8 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_inspector_no_tile_selected_label = memnew(Label);
tile_inspector_no_tile_selected_label->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER);
tile_inspector_no_tile_selected_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- tile_inspector_no_tile_selected_label->set_text(TTR("No tiles selected."));
+ tile_inspector_no_tile_selected_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
+ tile_inspector_no_tile_selected_label->set_text(TTR("No tiles selected.\nSelect one or more tiles from the palette to edit its properties."));
middle_vbox_container->add_child(tile_inspector_no_tile_selected_label);
// Property values palette.
@@ -2533,6 +2529,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
atlas_source_inspector = memnew(EditorInspector);
atlas_source_inspector->set_v_size_flags(SIZE_EXPAND_FILL);
atlas_source_inspector->set_show_categories(true);
+ atlas_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin));
atlas_source_inspector->edit(atlas_source_proxy_object);
middle_vbox_container->add_child(atlas_source_inspector);
@@ -2584,10 +2581,22 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view->set_h_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view->set_v_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
- tile_atlas_view->connect("transform_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_atlas_view_transform));
+ tile_atlas_view->connect("transform_changed", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::set_atlas_view_transform));
tile_atlas_view->connect("transform_changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_view_transform_changed).unbind(2));
right_panel->add_child(tile_atlas_view);
+ tile_create_help = memnew(HBoxContainer);
+ tile_atlas_view->add_child(tile_create_help);
+ tile_create_help->set_mouse_filter(MOUSE_FILTER_IGNORE);
+ tile_create_help->set_anchors_and_offsets_preset(Control::PRESET_BOTTOM_LEFT, Control::PRESET_MODE_MINSIZE, 30 * EDSCALE);
+ tile_create_help->add_theme_constant_override("separation", 30 * EDSCALE);
+
+ Label *help_label = memnew(Label(TTR("Hold Ctrl to create multiple tiles.")));
+ tile_create_help->add_child(help_label);
+
+ help_label = memnew(Label(TTR("Hold Shift to create big tiles.")));
+ tile_create_help->add_child(help_label);
+
base_tile_popup_menu = memnew(PopupMenu);
base_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE), TILE_DELETE);
base_tile_popup_menu->add_item(TTR("Create an Alternative Tile"), TILE_CREATE_ALTERNATIVE);
@@ -2599,7 +2608,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
empty_base_tile_popup_menu->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
tile_atlas_view->add_child(empty_base_tile_popup_menu);
- tile_atlas_control = memnew(Control);
+ tile_atlas_control = memnew(TileAtlasControl(this));
tile_atlas_control->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_control_draw));
tile_atlas_control->connect("mouse_exited", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_control_mouse_exited));
tile_atlas_control->connect("gui_input", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_control_gui_input));
@@ -2827,3 +2836,63 @@ bool EditorInspectorPluginTileData::parse_property(Object *p_object, const Varia
}
return false;
}
+
+Control::CursorShape TileSetAtlasSourceEditor::TileAtlasControl::get_cursor_shape(const Point2 &p_pos) const {
+ Control::CursorShape cursor_shape = get_default_cursor_shape();
+ if (editor->drag_type == DRAG_TYPE_NONE) {
+ if (editor->selection.size() == 1) {
+ // Change the cursor depending on the hovered thing.
+ TileSelection selected = editor->selection.front()->get();
+ if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) {
+ Transform2D xform = editor->tile_atlas_control->get_global_transform().affine_inverse() * get_global_transform();
+ Vector2 mouse_local_pos = xform.xform(p_pos);
+ Vector2i size_in_atlas = editor->tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
+ Rect2 region = editor->tile_set_atlas_source->get_tile_texture_region(selected.tile);
+ Size2 zoomed_size = editor->resize_handle->get_size() / editor->tile_atlas_view->get_zoom();
+ Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
+ const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
+ const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
+ bool can_grow[4];
+ for (int i = 0; i < 4; i++) {
+ can_grow[i] = editor->tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], editor->tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), editor->tile_set_atlas_source->get_tile_animation_columns(selected.tile), editor->tile_set_atlas_source->get_tile_animation_separation(selected.tile), editor->tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
+ can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
+ }
+ for (int i = 0; i < 4; i++) {
+ Vector2 pos = rect.position + rect.size * coords[i];
+ if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) {
+ cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
+ }
+ Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
+ if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
+ cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE;
+ }
+ }
+ }
+ }
+ } else {
+ switch (editor->drag_type) {
+ case DRAG_TYPE_RESIZE_TOP_LEFT:
+ case DRAG_TYPE_RESIZE_BOTTOM_RIGHT:
+ cursor_shape = CURSOR_FDIAGSIZE;
+ break;
+ case DRAG_TYPE_RESIZE_TOP:
+ case DRAG_TYPE_RESIZE_BOTTOM:
+ cursor_shape = CURSOR_VSIZE;
+ break;
+ case DRAG_TYPE_RESIZE_TOP_RIGHT:
+ case DRAG_TYPE_RESIZE_BOTTOM_LEFT:
+ cursor_shape = CURSOR_BDIAGSIZE;
+ break;
+ case DRAG_TYPE_RESIZE_LEFT:
+ case DRAG_TYPE_RESIZE_RIGHT:
+ cursor_shape = CURSOR_HSIZE;
+ break;
+ case DRAG_TYPE_MOVE_TILE:
+ cursor_shape = CURSOR_MOVE;
+ break;
+ default:
+ break;
+ }
+ }
+ return cursor_shape;
+}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index 65a2ba33f6..ff928ab2eb 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -112,6 +112,15 @@ public:
}
};
+ class TileAtlasControl : public Control {
+ TileSetAtlasSourceEditor *editor = nullptr;
+
+ public:
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
+ TileAtlasControl(TileSetAtlasSourceEditor *p_editor) { editor = p_editor; }
+ };
+ friend class TileAtlasControl;
+
private:
bool read_only = false;
@@ -150,6 +159,7 @@ private:
// -- Atlas view --
TileAtlasView *tile_atlas_view = nullptr;
+ HBoxContainer *tile_create_help = nullptr;
// Dragging
enum DragType {
@@ -261,6 +271,7 @@ private:
void _auto_create_tiles();
void _auto_remove_tiles();
AcceptDialog *confirm_auto_create_tiles = nullptr;
+ Vector2i _get_drag_offset_tile_coords(const Vector2i &p_offset) const;
void _tile_set_changed();
void _tile_proxy_object_changed(String p_what);
@@ -279,8 +290,6 @@ public:
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_source, int p_source_id);
void init_source();
- virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
-
TileSetAtlasSourceEditor();
~TileSetAtlasSourceEditor();
};
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index 23076266ee..f620e434ab 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -34,13 +34,16 @@
#include "tiles_editor_plugin.h"
#include "editor/editor_file_system.h"
+#include "editor/editor_inspector.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_file_dialog.h"
#include "scene/gui/box_container.h"
#include "scene/gui/control.h"
+#include "scene/gui/dialogs.h"
#include "scene/gui/tab_container.h"
TileSetEditor *TileSetEditor::singleton = nullptr;
@@ -148,7 +151,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
sources_list->clear();
// Update the atlas sources.
- List<int> source_ids = TilesEditorPlugin::get_singleton()->get_sorted_sources(tile_set);
+ List<int> source_ids = TilesEditorUtils::get_singleton()->get_sorted_sources(tile_set);
for (const int &source_id : source_ids) {
TileSetSource *source = *tile_set->get_source(source_id);
@@ -157,7 +160,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
// Common to all type of sources.
if (!source->get_name().is_empty()) {
- item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id);
+ item_text = source->get_name();
}
// Atlas source.
@@ -166,7 +169,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
texture = atlas_source->get_texture();
if (item_text.is_empty()) {
if (texture.is_valid()) {
- item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id);
+ item_text = texture->get_path().get_file();
} else {
item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
}
@@ -220,7 +223,31 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
_source_selected(sources_list->get_current());
// Synchronize the lists.
- TilesEditorPlugin::get_singleton()->set_sources_lists_current(sources_list->get_current());
+ TilesEditorUtils::get_singleton()->set_sources_lists_current(sources_list->get_current());
+}
+
+void TileSetEditor::_texture_file_selected(const String &p_path) {
+ Ref<Texture2D> texture = ResourceLoader::load(p_path);
+ if (texture.is_null()) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid texture selected."));
+ return;
+ }
+
+ int source_id = tile_set->get_next_source_id();
+
+ Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
+ atlas_source->set_texture(texture);
+
+ // Add a new source.
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Add atlas source"));
+ undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
+ undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
+ undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
+ undo_redo->commit_action();
+
+ _update_sources_list(source_id);
+ tile_set_atlas_source_editor->init_source();
}
void TileSetEditor::_source_selected(int p_source_index) {
@@ -278,19 +305,19 @@ void TileSetEditor::_source_add_id_pressed(int p_id_pressed) {
switch (p_id_pressed) {
case 0: {
- int source_id = tile_set->get_next_source_id();
-
- Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
-
- // Add a new source.
- EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
- undo_redo->create_action(TTR("Add atlas source"));
- undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
- undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
- undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
- undo_redo->commit_action();
-
- _update_sources_list(source_id);
+ if (!texture_file_dialog) {
+ texture_file_dialog = memnew(EditorFileDialog);
+ add_child(texture_file_dialog);
+ texture_file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
+ texture_file_dialog->connect("file_selected", callable_mp(this, &TileSetEditor::_texture_file_selected));
+
+ List<String> extensions;
+ ResourceLoader::get_recognized_extensions_for_type("Texture2D", &extensions);
+ for (const String &E : extensions) {
+ texture_file_dialog->add_filter("*." + E, E.to_upper());
+ }
+ }
+ texture_file_dialog->popup_file_dialog();
} break;
case 1: {
int source_id = tile_set->get_next_source_id();
@@ -327,8 +354,8 @@ void TileSetEditor::_sources_advanced_menu_id_pressed(int p_id_pressed) {
}
void TileSetEditor::_set_source_sort(int p_sort) {
- TilesEditorPlugin::get_singleton()->set_sorting_option(p_sort);
- for (int i = 0; i != TilesEditorPlugin::SOURCE_SORT_MAX; i++) {
+ TilesEditorUtils::get_singleton()->set_sorting_option(p_sort);
+ for (int i = 0; i != TilesEditorUtils::SOURCE_SORT_MAX; i++) {
source_sort_button->get_popup()->set_item_checked(i, (i == (int)p_sort));
}
@@ -345,13 +372,13 @@ void TileSetEditor::_set_source_sort(int p_sort) {
void TileSetEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
sources_delete_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
sources_add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
source_sort_button->set_icon(get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
sources_advanced_menu_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
missing_texture_texture = get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons"));
+ expanded_area->add_theme_style_override("panel", get_theme_stylebox("panel", "Tree"));
_update_sources_list();
} break;
@@ -376,6 +403,12 @@ void TileSetEditor::_notification(int p_what) {
tile_set_changed_needs_update = false;
}
} break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (!is_visible_in_tree()) {
+ remove_expanded_editor();
+ }
+ } break;
}
}
@@ -419,7 +452,7 @@ void TileSetEditor::_update_patterns_list() {
int id = patterns_item_list->add_item("");
patterns_item_list->set_item_metadata(id, tile_set->get_pattern(i));
patterns_item_list->set_item_tooltip(id, vformat(TTR("Index: %d"), i));
- TilesEditorPlugin::get_singleton()->queue_pattern_preview(tile_set, tile_set->get_pattern(i), callable_mp(this, &TileSetEditor::_pattern_preview_done));
+ TilesEditorUtils::get_singleton()->queue_pattern_preview(tile_set, tile_set->get_pattern(i), callable_mp(this, &TileSetEditor::_pattern_preview_done));
}
// Update the label visibility.
@@ -698,7 +731,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.
@@ -713,7 +746,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));
@@ -724,11 +757,70 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
}
}
+void TileSetEditor::add_expanded_editor(Control *p_editor) {
+ expanded_editor = p_editor;
+ expanded_editor_parent = p_editor->get_parent()->get_instance_id();
+
+ // Find the scrollable control this node belongs to.
+ Node *check_parent = expanded_editor->get_parent();
+ Control *parent_container = nullptr;
+ while (check_parent) {
+ parent_container = Object::cast_to<EditorInspector>(check_parent);
+ if (parent_container) {
+ break;
+ }
+ parent_container = Object::cast_to<ScrollContainer>(check_parent);
+ if (parent_container) {
+ break;
+ }
+ check_parent = check_parent->get_parent();
+ }
+ ERR_FAIL_NULL(parent_container);
+
+ expanded_editor->set_meta("reparented", true);
+ expanded_editor->reparent(expanded_area);
+ expanded_area->show();
+ expanded_area->set_size(Vector2(parent_container->get_global_rect().get_end().x - expanded_area->get_global_position().x, expanded_area->get_size().y));
+
+ for (SplitContainer *split : disable_on_expand) {
+ split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
+ }
+}
+
+void TileSetEditor::remove_expanded_editor() {
+ if (!expanded_editor) {
+ return;
+ }
+
+ Node *original_parent = Object::cast_to<Node>(ObjectDB::get_instance(expanded_editor_parent));
+ if (original_parent) {
+ expanded_editor->remove_meta("reparented");
+ expanded_editor->reparent(original_parent);
+ } else {
+ expanded_editor->queue_free();
+ }
+ expanded_editor = nullptr;
+ expanded_editor_parent = ObjectID();
+ expanded_area->hide();
+
+ for (SplitContainer *split : disable_on_expand) {
+ split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
+ }
+}
+
+void TileSetEditor::register_split(SplitContainer *p_split) {
+ disable_on_expand.push_back(p_split);
+}
+
TileSetEditor::TileSetEditor() {
singleton = this;
set_process_internal(true);
+ VBoxContainer *main_vb = memnew(VBoxContainer);
+ add_child(main_vb);
+ main_vb->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+
// TabBar.
tabs_bar = memnew(TabBar);
tabs_bar->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
@@ -740,7 +832,7 @@ TileSetEditor::TileSetEditor() {
tile_set_toolbar = memnew(HBoxContainer);
tile_set_toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
tile_set_toolbar->add_child(tabs_bar);
- add_child(tile_set_toolbar);
+ main_vb->add_child(tile_set_toolbar);
//// Tiles ////
// Split container.
@@ -748,7 +840,7 @@ TileSetEditor::TileSetEditor() {
split_container->set_name(TTR("Tiles"));
split_container->set_h_size_flags(SIZE_EXPAND_FILL);
split_container->set_v_size_flags(SIZE_EXPAND_FILL);
- add_child(split_container);
+ main_vb->add_child(split_container);
// Sources list.
VBoxContainer *split_container_left_side = memnew(VBoxContainer);
@@ -764,19 +856,19 @@ TileSetEditor::TileSetEditor() {
PopupMenu *p = source_sort_button->get_popup();
p->connect("id_pressed", callable_mp(this, &TileSetEditor::_set_source_sort));
- p->add_radio_check_item(TTR("Sort by ID (Ascending)"), TilesEditorPlugin::SOURCE_SORT_ID);
- p->add_radio_check_item(TTR("Sort by ID (Descending)"), TilesEditorPlugin::SOURCE_SORT_ID_REVERSE);
- p->add_radio_check_item(TTR("Sort by Name (Ascending)"), TilesEditorPlugin::SOURCE_SORT_NAME);
- p->add_radio_check_item(TTR("Sort by Name (Descending)"), TilesEditorPlugin::SOURCE_SORT_NAME_REVERSE);
- p->set_item_checked(TilesEditorPlugin::SOURCE_SORT_ID, true);
+ p->add_radio_check_item(TTR("Sort by ID (Ascending)"), TilesEditorUtils::SOURCE_SORT_ID);
+ p->add_radio_check_item(TTR("Sort by ID (Descending)"), TilesEditorUtils::SOURCE_SORT_ID_REVERSE);
+ p->add_radio_check_item(TTR("Sort by Name (Ascending)"), TilesEditorUtils::SOURCE_SORT_NAME);
+ p->add_radio_check_item(TTR("Sort by Name (Descending)"), TilesEditorUtils::SOURCE_SORT_NAME_REVERSE);
+ p->set_item_checked(TilesEditorUtils::SOURCE_SORT_ID, true);
sources_list = memnew(ItemList);
sources_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE);
sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
sources_list->set_v_size_flags(SIZE_EXPAND_FILL);
sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected));
- sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
- sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list).bind(sources_list, source_sort_button));
+ sources_list->connect("item_selected", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::set_sources_lists_current));
+ sources_list->connect("visibility_changed", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::synchronize_sources_list).bind(sources_list, source_sort_button));
sources_list->add_user_signal(MethodInfo("sort_request"));
sources_list->connect("sort_request", callable_mp(this, &TileSetEditor::_update_sources_list).bind(-1));
sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
@@ -855,7 +947,7 @@ TileSetEditor::TileSetEditor() {
patterns_item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size));
patterns_item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
patterns_item_list->connect("gui_input", callable_mp(this, &TileSetEditor::_patterns_item_list_gui_input));
- add_child(patterns_item_list);
+ main_vb->add_child(patterns_item_list);
patterns_item_list->hide();
patterns_help_label = memnew(Label);
@@ -864,7 +956,64 @@ TileSetEditor::TileSetEditor() {
patterns_help_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER);
patterns_item_list->add_child(patterns_help_label);
+ // Expanded editor
+ expanded_area = memnew(PanelContainer);
+ add_child(expanded_area);
+ expanded_area->set_anchors_and_offsets_preset(PRESET_LEFT_WIDE);
+ expanded_area->hide();
+
// Registers UndoRedo inspector callback.
EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element));
EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
}
+
+void TileSourceInspectorPlugin::_show_id_edit_dialog(Object *p_for_source) {
+ if (!id_edit_dialog) {
+ id_edit_dialog = memnew(ConfirmationDialog);
+ TileSetEditor::get_singleton()->add_child(id_edit_dialog);
+
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ id_edit_dialog->add_child(vbox);
+
+ Label *label = memnew(Label(TTR("Warning: Modifying a source ID will result in all TileMaps using that source to reference an invalid source instead. This may result in unexpected data loss. Change this ID carefully.")));
+ label->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
+ vbox->add_child(label);
+
+ id_input = memnew(SpinBox);
+ vbox->add_child(id_input);
+ id_input->set_max(INT_MAX);
+
+ id_edit_dialog->connect("confirmed", callable_mp(this, &TileSourceInspectorPlugin::_confirm_change_id));
+ }
+ edited_source = p_for_source;
+ id_input->set_value(p_for_source->get("id"));
+ id_edit_dialog->popup_centered(Vector2i(400, 0) * EDSCALE);
+ callable_mp((Control *)id_input->get_line_edit(), &Control::grab_focus).call_deferred();
+}
+
+void TileSourceInspectorPlugin::_confirm_change_id() {
+ edited_source->set("id", id_input->get_value());
+ id_label->set_text(vformat(TTR("ID: %d"), edited_source->get("id"))); // Use get(), because the provided ID might've been invalid.
+}
+
+bool TileSourceInspectorPlugin::can_handle(Object *p_object) {
+ return p_object->is_class("TileSetAtlasSourceProxyObject") || p_object->is_class("TileSetScenesCollectionProxyObject");
+}
+
+bool TileSourceInspectorPlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
+ if (p_path == "id") {
+ HBoxContainer *hbox = memnew(HBoxContainer);
+ hbox->set_alignment(BoxContainer::ALIGNMENT_CENTER);
+
+ id_label = memnew(Label(vformat(TTR("ID: %d"), p_object->get("id"))));
+ hbox->add_child(id_label);
+
+ Button *button = memnew(Button(TTR("Edit")));
+ hbox->add_child(button);
+ button->connect("pressed", callable_mp(this, &TileSourceInspectorPlugin::_show_id_edit_dialog).bind(p_object));
+
+ add_custom_control(hbox);
+ return true;
+ }
+ return false;
+}
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index d36d3bde41..86cd70d19e 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -32,15 +32,21 @@
#define TILE_SET_EDITOR_H
#include "atlas_merging_dialog.h"
-#include "scene/gui/box_container.h"
#include "scene/gui/tab_bar.h"
#include "scene/resources/tile_set.h"
#include "tile_proxies_manager_dialog.h"
#include "tile_set_atlas_source_editor.h"
#include "tile_set_scenes_collection_source_editor.h"
-class TileSetEditor : public VBoxContainer {
- GDCLASS(TileSetEditor, VBoxContainer);
+class AcceptDialog;
+class SpinBox;
+class HBoxContainer;
+class SplitContainer;
+class EditorFileDialog;
+class EditorInspectorPlugin;
+
+class TileSetEditor : public Control {
+ GDCLASS(TileSetEditor, Control);
static TileSetEditor *singleton;
@@ -72,12 +78,14 @@ private:
MenuButton *sources_advanced_menu_button = nullptr;
ItemList *sources_list = nullptr;
Ref<Texture2D> missing_texture_texture;
+ void _texture_file_selected(const String &p_path);
void _source_selected(int p_source_index);
void _source_delete_pressed();
void _source_add_id_pressed(int p_id_pressed);
void _sources_advanced_menu_id_pressed(int p_id_pressed);
void _set_source_sort(int p_sort);
+ EditorFileDialog *texture_file_dialog = nullptr;
AtlasMergingDialog *atlas_merging_dialog = nullptr;
TileProxiesManagerDialog *tile_proxies_manager_dialog = nullptr;
@@ -91,6 +99,12 @@ private:
bool select_last_pattern = false;
void _update_patterns_list();
+ // Expanded editor.
+ PanelContainer *expanded_area = nullptr;
+ Control *expanded_editor = nullptr;
+ ObjectID expanded_editor_parent;
+ LocalVector<SplitContainer *> disable_on_expand;
+
void _tile_set_changed();
void _tab_changed(int p_tab_changed);
@@ -105,7 +119,27 @@ public:
void edit(Ref<TileSet> p_tile_set);
+ void add_expanded_editor(Control *p_editor);
+ void remove_expanded_editor();
+ void register_split(SplitContainer *p_split);
+
TileSetEditor();
};
+class TileSourceInspectorPlugin : public EditorInspectorPlugin {
+ GDCLASS(TileSourceInspectorPlugin, EditorInspectorPlugin);
+
+ AcceptDialog *id_edit_dialog = nullptr;
+ Label *id_label = nullptr;
+ SpinBox *id_input = nullptr;
+ Object *edited_source = nullptr;
+
+ void _show_id_edit_dialog(Object *p_for_source);
+ void _confirm_change_id();
+
+public:
+ virtual bool can_handle(Object *p_object) override;
+ virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
+};
+
#endif // TILE_SET_EDITOR_H
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..13270f3821 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -36,6 +36,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/plugins/tiles/tile_set_editor.h"
#include "scene/gui/button.h"
#include "scene/gui/item_list.h"
@@ -384,7 +385,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 +405,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.
@@ -504,6 +505,7 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
scenes_collection_source_inspector = memnew(EditorInspector);
scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+ scenes_collection_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin));
scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object);
middle_vbox_container->add_child(scenes_collection_source_inspector);
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index b2ee3103ce..911316822a 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/inspector_dock.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "scene/2d/tile_map.h"
@@ -45,25 +46,28 @@
#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;
+TilesEditorUtils *TilesEditorUtils::singleton = nullptr;
+TileMapEditorPlugin *tile_map_plugin_singleton = nullptr;
+TileSetEditorPlugin *tile_set_plugin_singleton = nullptr;
-void TilesEditorPlugin::_preview_frame_started() {
- RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<TilesEditorPlugin *>(this), &TilesEditorPlugin::_pattern_preview_done));
+void TilesEditorUtils::_preview_frame_started() {
+ RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<TilesEditorUtils *>(this), &TilesEditorUtils::_pattern_preview_done));
}
-void TilesEditorPlugin::_pattern_preview_done() {
+void TilesEditorUtils::_pattern_preview_done() {
pattern_preview_done.post();
}
-void TilesEditorPlugin::_thread_func(void *ud) {
- TilesEditorPlugin *te = static_cast<TilesEditorPlugin *>(ud);
+void TilesEditorUtils::_thread_func(void *ud) {
+ TilesEditorUtils *te = static_cast<TilesEditorUtils *>(ud);
set_current_thread_safe_for_nodes(true);
te->_thread();
}
-void TilesEditorPlugin::_thread() {
+void TilesEditorUtils::_thread() {
pattern_thread_exited.clear();
while (!pattern_thread_exit.is_set()) {
pattern_preview_sem.wait();
@@ -123,7 +127,7 @@ void TilesEditorPlugin::_thread() {
// Add the viewport at the last moment to avoid rendering too early.
EditorNode::get_singleton()->call_deferred("add_child", viewport);
- RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<TilesEditorPlugin *>(this), &TilesEditorPlugin::_preview_frame_started), Object::CONNECT_ONE_SHOT);
+ RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<TilesEditorUtils *>(this), &TilesEditorUtils::_preview_frame_started), Object::CONNECT_ONE_SHOT);
pattern_preview_done.wait();
@@ -143,85 +147,7 @@ void TilesEditorPlugin::_thread() {
pattern_thread_exited.set();
}
-void TilesEditorPlugin::_tile_map_changed() {
- tile_map_changed_needs_update = true;
-}
-
-void TilesEditorPlugin::_update_editors() {
- // If tile_map is not edited, we change the edited only if we are not editing a tile_set.
- if (tile_set.is_valid()) {
- tileset_editor->edit(tile_set);
- }
-
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (tile_map) {
- tilemap_editor->edit(tile_map);
- } else {
- tilemap_editor->edit(nullptr);
- }
-
- // Update the viewport.
- CanvasItemEditor::get_singleton()->update_viewport();
-
- // Make sure the tile set editor is visible if we have one assigned.
- tileset_editor_button->set_visible(is_visible && tile_set.is_valid());
- tilemap_editor_button->set_visible(is_visible && tile_map);
-
- // Update visibility of bottom panel buttons.
- if (tileset_editor_button->is_pressed() && !tile_set.is_valid()) {
- if (tile_map) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(tilemap_editor);
- } else {
- EditorNode::get_singleton()->hide_bottom_panel();
- }
- }
-}
-
-void TilesEditorPlugin::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_INTERNAL_PROCESS: {
- if (tile_map_changed_needs_update) {
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (tile_map) {
- tile_set = tile_map->get_tileset();
- }
- _update_editors();
- tile_map_changed_needs_update = false;
- }
- } break;
- }
-}
-
-void TilesEditorPlugin::make_visible(bool p_visible) {
- if (p_visible || is_tile_map_selected()) {
- // Disable and hide invalid editors.
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- tileset_editor_button->set_visible(tile_set.is_valid());
- tilemap_editor_button->set_visible(tile_map);
- if (tile_map && (!is_editing_tile_set || !p_visible)) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(tilemap_editor);
- } else {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(tileset_editor);
- }
- is_visible = true;
- } else {
- tileset_editor_button->hide();
- tilemap_editor_button->hide();
- EditorNode::get_singleton()->hide_bottom_panel();
- is_visible = false;
- }
-}
-
-bool TilesEditorPlugin::is_tile_map_selected() {
- TypedArray<Node> selection = EditorInterface::get_singleton()->get_selection()->get_selected_nodes();
- if (selection.size() == 1 && Object::cast_to<TileMap>(selection[0])) {
- return true;
- }
-
- return false;
-}
-
-void TilesEditorPlugin::queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback) {
+void TilesEditorUtils::queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback) {
ERR_FAIL_COND(!p_tile_set.is_valid());
ERR_FAIL_COND(!p_pattern.is_valid());
{
@@ -231,11 +157,11 @@ void TilesEditorPlugin::queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileM
pattern_preview_sem.post();
}
-void TilesEditorPlugin::set_sources_lists_current(int p_current) {
+void TilesEditorUtils::set_sources_lists_current(int p_current) {
atlas_sources_lists_current = p_current;
}
-void TilesEditorPlugin::synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button) {
+void TilesEditorUtils::synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button) {
ItemList *item_list = Object::cast_to<ItemList>(p_current_list);
MenuButton *sorting_button = Object::cast_to<MenuButton>(p_current_sort_button);
ERR_FAIL_COND(!item_list);
@@ -263,12 +189,12 @@ void TilesEditorPlugin::synchronize_sources_list(Object *p_current_list, Object
}
}
-void TilesEditorPlugin::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
+void TilesEditorUtils::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
atlas_view_zoom = p_zoom;
atlas_view_scroll = p_scroll;
}
-void TilesEditorPlugin::synchronize_atlas_view(Object *p_current) {
+void TilesEditorUtils::synchronize_atlas_view(Object *p_current) {
TileAtlasView *tile_atlas_view = Object::cast_to<TileAtlasView>(p_current);
ERR_FAIL_COND(!tile_atlas_view);
@@ -277,11 +203,11 @@ void TilesEditorPlugin::synchronize_atlas_view(Object *p_current) {
}
}
-void TilesEditorPlugin::set_sorting_option(int p_option) {
+void TilesEditorUtils::set_sorting_option(int p_option) {
source_sort = p_option;
}
-List<int> TilesEditorPlugin::get_sorted_sources(const Ref<TileSet> p_tile_set) const {
+List<int> TilesEditorUtils::get_sorted_sources(const Ref<TileSet> p_tile_set) const {
SourceNameComparator::tile_set = p_tile_set;
List<int> source_ids;
@@ -309,9 +235,9 @@ List<int> TilesEditorPlugin::get_sorted_sources(const Ref<TileSet> p_tile_set) c
return source_ids;
}
-Ref<TileSet> TilesEditorPlugin::SourceNameComparator::tile_set;
+Ref<TileSet> TilesEditorUtils::SourceNameComparator::tile_set;
-bool TilesEditorPlugin::SourceNameComparator::operator()(const int &p_a, const int &p_b) const {
+bool TilesEditorUtils::SourceNameComparator::operator()(const int &p_a, const int &p_b) const {
String name_a;
String name_b;
@@ -358,104 +284,165 @@ bool TilesEditorPlugin::SourceNameComparator::operator()(const int &p_a, const i
return NaturalNoCaseComparator()(name_a, name_b);
}
-void TilesEditorPlugin::edit(Object *p_object) {
- // Disconnect to changes.
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (tile_map) {
- tile_map->disconnect("changed", callable_mp(this, &TilesEditorPlugin::_tile_map_changed));
- }
+void TilesEditorUtils::display_tile_set_editor_panel() {
+ tile_map_plugin_singleton->hide_editor();
+ tile_set_plugin_singleton->make_visible(true);
+}
- // Update edited objects.
- tile_set = Ref<TileSet>();
- is_editing_tile_set = false;
-
- if (p_object) {
- if (p_object->is_class("TileMap")) {
- tile_map_id = p_object->get_instance_id();
- tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- tile_set = tile_map->get_tileset();
- EditorNode::get_singleton()->make_bottom_panel_item_visible(tilemap_editor);
- } else if (p_object->is_class("TileSet")) {
- tile_set = Ref<TileSet>(p_object);
- if (tile_map) {
- if (tile_map->get_tileset() != tile_set || !tile_map->is_inside_tree() || !is_tile_map_selected()) {
- tile_map = nullptr;
- tile_map_id = ObjectID();
- }
- }
- is_editing_tile_set = true;
+void TilesEditorUtils::draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color) {
+ real_t scale = p_ci->get_global_transform().get_scale().x * 0.5;
+ p_ci->draw_set_transform(p_rect.position, 0, Vector2(1, 1) / scale);
+ RS::get_singleton()->canvas_item_add_nine_patch(
+ p_ci->get_canvas_item(), Rect2(Vector2(), p_rect.size * scale), Rect2(), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("TileSelection"), SNAME("EditorIcons"))->get_rid(),
+ Vector2(2, 2), Vector2(2, 2), RS::NINE_PATCH_STRETCH, RS::NINE_PATCH_STRETCH, false, p_color);
+ p_ci->draw_set_transform_matrix(Transform2D());
+}
+
+TilesEditorUtils::TilesEditorUtils() {
+ singleton = this;
+ // Pattern preview generation thread.
+ pattern_preview_thread.start(_thread_func, this);
+}
+
+TilesEditorUtils::~TilesEditorUtils() {
+ if (pattern_preview_thread.is_started()) {
+ pattern_thread_exit.set();
+ pattern_preview_sem.post();
+ while (!pattern_thread_exited.is_set()) {
+ OS::get_singleton()->delay_usec(10000);
+ RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
}
+ pattern_preview_thread.wait_to_finish();
+ }
+ singleton = nullptr;
+}
+
+void TileMapEditorPlugin::_tile_map_changed() {
+ if (tile_map_changed_needs_update) {
+ return;
+ }
+ tile_map_changed_needs_update = true;
+ callable_mp(this, &TileMapEditorPlugin::_update_tile_map).call_deferred();
+}
+
+void TileMapEditorPlugin::_update_tile_map() {
+ if (tile_map && tile_map->get_tileset().is_valid()) {
+ EditorNode::get_singleton()->edit_item(tile_map->get_tileset().ptr(), InspectorDock::get_inspector_singleton());
}
+ tile_map_changed_needs_update = false;
+}
- // Update the editors.
- _update_editors();
+void TileMapEditorPlugin::_notification(int p_notification) {
+ if (p_notification == NOTIFICATION_EXIT_TREE) {
+ get_tree()->queue_delete(TilesEditorUtils::get_singleton());
+ }
+}
- // If the tileset is being edited, the visibility function must be called
- // here after _update_editors has been called.
- if (is_editing_tile_set) {
- EditorNode::get_singleton()->make_bottom_panel_item_visible(tileset_editor);
+void TileMapEditorPlugin::edit(Object *p_object) {
+ if (tile_map) {
+ tile_map->disconnect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_changed));
}
- // Add change listener.
+ tile_map = Object::cast_to<TileMap>(p_object);
+
+ editor->edit(tile_map);
if (tile_map) {
- tile_map->connect("changed", callable_mp(this, &TilesEditorPlugin::_tile_map_changed));
+ tile_map->connect("changed", callable_mp(this, &TileMapEditorPlugin::_tile_map_changed));
+
+ if (tile_map->get_tileset().is_valid()) {
+ EditorNode::get_singleton()->edit_item(tile_map->get_tileset().ptr(), InspectorDock::get_inspector_singleton());
+ }
}
}
-bool TilesEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("TileMap") || p_object->is_class("TileSet");
+bool TileMapEditorPlugin::handles(Object *p_object) const {
+ return Object::cast_to<TileMap>(p_object) != nullptr;
}
-void TilesEditorPlugin::draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color) {
- real_t scale = p_ci->get_global_transform().get_scale().x * 0.5;
- p_ci->draw_set_transform(p_rect.position, 0, Vector2(1, 1) / scale);
- RS::get_singleton()->canvas_item_add_nine_patch(
- p_ci->get_canvas_item(), Rect2(Vector2(), p_rect.size * scale), Rect2(), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("TileSelection"), SNAME("EditorIcons"))->get_rid(),
- Vector2(2, 2), Vector2(2, 2), RS::NINE_PATCH_STRETCH, RS::NINE_PATCH_STRETCH, false, p_color);
- p_ci->draw_set_transform_matrix(Transform2D());
+void TileMapEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ button->show();
+ EditorNode::get_singleton()->make_bottom_panel_item_visible(editor);
+ } else {
+ button->hide();
+ if (editor->is_visible_in_tree()) {
+ EditorNode::get_singleton()->hide_bottom_panel();
+ }
+ }
}
-TilesEditorPlugin::TilesEditorPlugin() {
- set_process_internal(true);
+bool TileMapEditorPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
+ return editor->forward_canvas_gui_input(p_event);
+}
- // Update the singleton.
- singleton = this;
+void TileMapEditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
+ editor->forward_canvas_draw_over_viewport(p_overlay);
+}
- // Tileset editor.
- tileset_editor = memnew(TileSetEditor);
- tileset_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- tileset_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- tileset_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
- tileset_editor->hide();
+void TileMapEditorPlugin::hide_editor() {
+ if (editor->is_visible_in_tree()) {
+ EditorNode::get_singleton()->hide_bottom_panel();
+ }
+}
- // Tilemap editor.
- tilemap_editor = memnew(TileMapEditor);
- tilemap_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- tilemap_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- tilemap_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
- tilemap_editor->hide();
+bool TileMapEditorPlugin::is_editor_visible() const {
+ return editor->is_visible_in_tree();
+}
- // Pattern preview generation thread.
- pattern_preview_thread.start(_thread_func, this);
+TileMapEditorPlugin::TileMapEditorPlugin() {
+ memnew(TilesEditorUtils);
+ tile_map_plugin_singleton = this;
- // Bottom buttons.
- tileset_editor_button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TileSet"), tileset_editor);
- tileset_editor_button->hide();
- tilemap_editor_button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TileMap"), tilemap_editor);
- tilemap_editor_button->hide();
+ editor = memnew(TileMapEditor);
+ editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
+ editor->hide();
- // Initialization.
- _update_editors();
+ button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TileMap"), editor);
+ button->hide();
}
-TilesEditorPlugin::~TilesEditorPlugin() {
- if (pattern_preview_thread.is_started()) {
- pattern_thread_exit.set();
- pattern_preview_sem.post();
- while (!pattern_thread_exited.is_set()) {
- OS::get_singleton()->delay_usec(10000);
- RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
+TileMapEditorPlugin::~TileMapEditorPlugin() {
+ tile_map_plugin_singleton = nullptr;
+}
+
+void TileSetEditorPlugin::edit(Object *p_object) {
+ editor->edit(Ref<TileSet>(p_object));
+}
+
+bool TileSetEditorPlugin::handles(Object *p_object) const {
+ return Object::cast_to<TileSet>(p_object) != nullptr;
+}
+
+void TileSetEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ button->show();
+ if (!tile_map_plugin_singleton->is_editor_visible()) {
+ EditorNode::get_singleton()->make_bottom_panel_item_visible(editor);
+ }
+ } else {
+ button->hide();
+ if (editor->is_visible_in_tree()) {
+ EditorNode::get_singleton()->hide_bottom_panel();
}
- pattern_preview_thread.wait_to_finish();
}
}
+
+TileSetEditorPlugin::TileSetEditorPlugin() {
+ DEV_ASSERT(tile_map_plugin_singleton);
+ tile_set_plugin_singleton = this;
+
+ editor = memnew(TileSetEditor);
+ editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
+ editor->hide();
+
+ button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TileSet"), editor);
+ button->hide();
+}
+
+TileSetEditorPlugin::~TileSetEditorPlugin() {
+ tile_set_plugin_singleton = nullptr;
+}
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index 50073e59c6..0bb45b746d 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -38,10 +38,10 @@
#include "tile_map_editor.h"
#include "tile_set_editor.h"
-class TilesEditorPlugin : public EditorPlugin {
- GDCLASS(TilesEditorPlugin, EditorPlugin);
+class TilesEditorUtils : public Object {
+ GDCLASS(TilesEditorUtils, Object);
- static TilesEditorPlugin *singleton;
+ static TilesEditorUtils *singleton;
public:
enum SourceSortOption {
@@ -53,28 +53,11 @@ public:
};
private:
- bool is_visible = false;
-
- bool tile_map_changed_needs_update = false;
- ObjectID tile_map_id;
- Ref<TileSet> tile_set;
- bool is_editing_tile_set = false;
-
- Button *tilemap_editor_button = nullptr;
- TileMapEditor *tilemap_editor = nullptr;
-
- Button *tileset_editor_button = nullptr;
- TileSetEditor *tileset_editor = nullptr;
-
- void _update_editors();
-
// For synchronization.
int atlas_sources_lists_current = 0;
float atlas_view_zoom = 1.0;
Vector2 atlas_view_scroll;
- void _tile_map_changed();
-
// Source sorting.
int source_sort = SOURCE_SORT_ID;
@@ -101,16 +84,8 @@ private:
static void _thread_func(void *ud);
void _thread();
-protected:
- void _notification(int p_what);
-
public:
- _FORCE_INLINE_ static TilesEditorPlugin *get_singleton() { return singleton; }
-
- virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return tilemap_editor->forward_canvas_gui_input(p_event); }
- virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { tilemap_editor->forward_canvas_draw_over_viewport(p_overlay); }
-
- bool is_tile_map_selected();
+ _FORCE_INLINE_ static TilesEditorUtils *get_singleton() { return singleton; }
// Pattern preview API.
void queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback);
@@ -126,14 +101,57 @@ public:
void set_sorting_option(int p_option);
List<int> get_sorted_sources(const Ref<TileSet> p_tile_set) const;
+ // Misc.
+ void display_tile_set_editor_panel();
+
+ static void draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color = Color(1.0, 1.0, 1.0));
+
+ TilesEditorUtils();
+ ~TilesEditorUtils();
+};
+
+class TileMapEditorPlugin : public EditorPlugin {
+ GDCLASS(TileMapEditorPlugin, EditorPlugin);
+
+ TileMapEditor *editor = nullptr;
+ Button *button = nullptr;
+ TileMap *tile_map = nullptr;
+
+ bool tile_map_changed_needs_update = false;
+ void _tile_map_changed();
+ void _update_tile_map();
+
+protected:
+ void _notification(int p_notification);
+
+public:
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
- static void draw_selection_rect(CanvasItem *p_ci, const Rect2 &p_rect, const Color &p_color = Color(1.0, 1.0, 1.0));
+ virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
+ virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
+
+ void hide_editor();
+ bool is_editor_visible() const;
+
+ TileMapEditorPlugin();
+ ~TileMapEditorPlugin();
+};
+
+class TileSetEditorPlugin : public EditorPlugin {
+ GDCLASS(TileSetEditorPlugin, EditorPlugin);
+
+ TileSetEditor *editor = nullptr;
+ Button *button = nullptr;
+
+public:
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
- TilesEditorPlugin();
- ~TilesEditorPlugin();
+ TileSetEditorPlugin();
+ ~TileSetEditorPlugin();
};
#endif // TILES_EDITOR_PLUGIN_H
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index c9651e634f..3062059001 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();
}
}
@@ -426,7 +428,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(vsnode.ptr());
bool is_group = !group_node.is_null();
- bool is_comment = false;
+ Ref<VisualShaderNodeComment> comment_node = Object::cast_to<VisualShaderNodeComment>(vsnode.ptr());
+ bool is_comment = comment_node.is_valid();
Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(group_node.ptr());
bool is_expression = !expression_node.is_null();
@@ -465,6 +468,10 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
expression = expression_node->get_expression();
}
+ if (is_comment) {
+ node->set_visible(false);
+ }
+
node->set_position_offset(visual_shader->get_node_position(p_type, p_id));
node->set_title(vsnode->get_caption());
node->set_name(itos(p_id));
@@ -488,17 +495,6 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
}
if (is_resizable) {
- Ref<VisualShaderNodeComment> comment_node = Object::cast_to<VisualShaderNodeComment>(vsnode.ptr());
- if (comment_node.is_valid()) {
- is_comment = true;
- node->set_comment(true);
-
- Label *comment_label = memnew(Label);
- node->add_child(comment_label);
- comment_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- comment_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- comment_label->set_text(comment_node->get_description());
- }
editor->call_deferred(SNAME("_set_node_size"), (int)p_type, p_id, size);
}
@@ -560,9 +556,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 +573,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 +1153,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->set_graph_offset(graph->get_scroll_ofs() / EDSCALE);
+ visual_shader->connect_changed(callable_mp(this, &VisualShaderEditor::_update_preview));
+ visual_shader->set_graph_offset(graph->get_scroll_offset() / 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();
}
@@ -2031,7 +2019,7 @@ void VisualShaderEditor::_update_graph() {
return;
}
- graph->set_scroll_ofs(visual_shader->get_graph_offset() * EDSCALE);
+ graph->set_scroll_offset(visual_shader->get_graph_offset() * EDSCALE);
VisualShader::Type type = get_current_shader_type();
@@ -3089,6 +3077,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);
@@ -3099,7 +3090,7 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
bool is_curve_xyz = (Object::cast_to<VisualShaderNodeCurveXYZTexture>(vsnode.ptr()) != nullptr);
bool is_parameter = (Object::cast_to<VisualShaderNodeParameter>(vsnode.ptr()) != nullptr);
- Point2 position = graph->get_scroll_ofs();
+ Point2 position = graph->get_scroll_offset();
if (saved_node_pos_dirty) {
position += saved_node_pos;
@@ -3217,16 +3208,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) {
@@ -4386,7 +4387,7 @@ void VisualShaderEditor::_paste_nodes(bool p_use_custom_position, const Vector2
mpos = graph->get_local_mouse_position();
}
- _dup_paste_nodes(type, copy_items_buffer, copy_connections_buffer, graph->get_scroll_ofs() / scale + mpos / scale - selection_center, false);
+ _dup_paste_nodes(type, copy_items_buffer, copy_connections_buffer, graph->get_scroll_offset() / scale + mpos / scale - selection_center, false);
}
void VisualShaderEditor::_mode_selected(int p_id) {
@@ -5089,7 +5090,7 @@ VisualShaderEditor::VisualShaderEditor() {
FileSystemDock::get_singleton()->connect("resource_removed", callable_mp(this, &VisualShaderEditor::_resource_removed));
graph = memnew(GraphEdit);
- graph->get_zoom_hbox()->set_h_size_flags(SIZE_EXPAND_FILL);
+ graph->get_menu_hbox()->set_h_size_flags(SIZE_EXPAND_FILL);
graph->set_v_size_flags(SIZE_EXPAND_FILL);
graph->set_h_size_flags(SIZE_EXPAND_FILL);
graph->set_show_zoom_label(true);
@@ -5182,8 +5183,8 @@ VisualShaderEditor::VisualShaderEditor() {
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SAMPLER, VisualShaderNode::PORT_TYPE_SAMPLER);
VSeparator *vs = memnew(VSeparator);
- graph->get_zoom_hbox()->add_child(vs);
- graph->get_zoom_hbox()->move_child(vs, 0);
+ graph->get_menu_hbox()->add_child(vs);
+ graph->get_menu_hbox()->move_child(vs, 0);
custom_mode_box = memnew(CheckBox);
custom_mode_box->set_text(TTR("Custom"));
@@ -5217,28 +5218,28 @@ VisualShaderEditor::VisualShaderEditor() {
edit_type = edit_type_standard;
- graph->get_zoom_hbox()->add_child(custom_mode_box);
- graph->get_zoom_hbox()->move_child(custom_mode_box, 0);
- graph->get_zoom_hbox()->add_child(edit_type_standard);
- graph->get_zoom_hbox()->move_child(edit_type_standard, 0);
- graph->get_zoom_hbox()->add_child(edit_type_particles);
- graph->get_zoom_hbox()->move_child(edit_type_particles, 0);
- graph->get_zoom_hbox()->add_child(edit_type_sky);
- graph->get_zoom_hbox()->move_child(edit_type_sky, 0);
- graph->get_zoom_hbox()->add_child(edit_type_fog);
- graph->get_zoom_hbox()->move_child(edit_type_fog, 0);
+ graph->get_menu_hbox()->add_child(custom_mode_box);
+ graph->get_menu_hbox()->move_child(custom_mode_box, 0);
+ graph->get_menu_hbox()->add_child(edit_type_standard);
+ graph->get_menu_hbox()->move_child(edit_type_standard, 0);
+ graph->get_menu_hbox()->add_child(edit_type_particles);
+ graph->get_menu_hbox()->move_child(edit_type_particles, 0);
+ graph->get_menu_hbox()->add_child(edit_type_sky);
+ graph->get_menu_hbox()->move_child(edit_type_sky, 0);
+ graph->get_menu_hbox()->add_child(edit_type_fog);
+ graph->get_menu_hbox()->move_child(edit_type_fog, 0);
add_node = memnew(Button);
add_node->set_flat(true);
add_node->set_text(TTR("Add Node..."));
- graph->get_zoom_hbox()->add_child(add_node);
- graph->get_zoom_hbox()->move_child(add_node, 0);
+ graph->get_menu_hbox()->add_child(add_node);
+ graph->get_menu_hbox()->move_child(add_node, 0);
add_node->connect("pressed", callable_mp(this, &VisualShaderEditor::_show_members_dialog).bind(false, VisualShaderNode::PORT_TYPE_MAX, VisualShaderNode::PORT_TYPE_MAX));
varying_button = memnew(MenuButton);
varying_button->set_text(TTR("Manage Varyings"));
varying_button->set_switch_on_hover(true);
- graph->get_zoom_hbox()->add_child(varying_button);
+ graph->get_menu_hbox()->add_child(varying_button);
PopupMenu *varying_menu = varying_button->get_popup();
varying_menu->add_item(TTR("Add Varying"), int(VaryingMenuOptions::ADD));
@@ -5249,7 +5250,7 @@ VisualShaderEditor::VisualShaderEditor() {
preview_shader->set_flat(true);
preview_shader->set_toggle_mode(true);
preview_shader->set_tooltip_text(TTR("Show generated shader code."));
- graph->get_zoom_hbox()->add_child(preview_shader);
+ graph->get_menu_hbox()->add_child(preview_shader);
preview_shader->connect("pressed", callable_mp(this, &VisualShaderEditor::_show_preview_text));
///////////////////////////////////////
@@ -5874,6 +5875,10 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("CurveXYZTexture", "Textures/Functions", "VisualShaderNodeCurveXYZTexture", TTR("Perform the three components curve texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("LinearSceneDepth", "Textures/Functions", "VisualShaderNodeLinearSceneDepth", TTR("Returns the depth value obtained from the depth prepass in a linear space."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
texture2d_node_option_idx = add_options.size();
+ add_options.push_back(AddOption("WorldPositionFromDepth", "Textures/Functions", "VisualShaderNodeWorldPositionFromDepth", TTR("Reconstructs the World Position of the Node from the depth texture."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ texture2d_node_option_idx = add_options.size();
+ add_options.push_back(AddOption("ScreenNormalWorldSpace", "Textures/Functions", "VisualShaderNodeScreenNormalWorldSpace", TTR("Unpacks the Screen Normal Texture in World Space"), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ texture2d_node_option_idx = add_options.size();
add_options.push_back(AddOption("Texture2D", "Textures/Functions", "VisualShaderNodeTexture", TTR("Perform the 2D texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D));
texture2d_array_node_option_idx = add_options.size();
add_options.push_back(AddOption("Texture2DArray", "Textures/Functions", "VisualShaderNodeTexture2DArray", TTR("Perform the 2D-array texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D));
@@ -5918,6 +5923,8 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("ProximityFade", "Utility", "VisualShaderNodeProximityFade", TTR("The proximity fade effect fades out each pixel based on its distance to another object."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("RandomRange", "Utility", "VisualShaderNodeRandomRange", TTR("Returns a random value between the minimum and maximum input values."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Remap", "Utility", "VisualShaderNodeRemap", TTR("Remaps a given input from the input range to the output range."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("RotationByAxis", "Utility", "VisualShaderNodeRotationByAxis", TTR("Rotates an input vector by a given angle."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("RotationByAxis", "Utility", "VisualShaderNodeRotationByAxis", TTR("Rotates an input vector by a given angle."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
// VECTOR
@@ -6120,7 +6127,6 @@ VisualShaderEditor::VisualShaderEditor() {
// SPECIAL
- add_options.push_back(AddOption("Comment", "Special", "VisualShaderNodeComment", TTR("A rectangular area with a description string for better graph organization.")));
add_options.push_back(AddOption("Expression", "Special", "VisualShaderNodeExpression", TTR("Custom Godot Shader Language expression, with custom amount of input and output ports. This is a direct injection of code into the vertex/fragment/light function, do not use it to write the function declarations inside.")));
add_options.push_back(AddOption("GlobalExpression", "Special", "VisualShaderNodeGlobalExpression", TTR("Custom Godot Shader Language expression, which is placed on top of the resulted shader. You can place various function definitions inside and call it later in the Expressions. You can also declare varyings, parameters and constants.")));
add_options.push_back(AddOption("ParameterRef", "Special", "VisualShaderNodeParameterRef", TTR("A reference to an existing parameter.")));
@@ -6447,7 +6453,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 +6719,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/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp
index 5ae5468e4b..43b133f4b5 100644
--- a/editor/plugins/voxel_gi_editor_plugin.cpp
+++ b/editor/plugins/voxel_gi_editor_plugin.cpp
@@ -166,8 +166,13 @@ void VoxelGIEditorPlugin::_voxel_gi_save_path_and_bake(const String &p_path) {
probe_file->hide();
if (voxel_gi) {
voxel_gi->bake();
- ERR_FAIL_COND(voxel_gi->get_probe_data().is_null());
- ResourceSaver::save(voxel_gi->get_probe_data(), p_path, ResourceSaver::FLAG_CHANGE_PATH);
+ // Ensure the VoxelGIData is always saved to an external resource.
+ // This avoids bloating the scene file with large binary data,
+ // which would be serialized as Base64 if the scene is a `.tscn` file.
+ Ref<VoxelGIData> voxel_gi_data = voxel_gi->get_probe_data();
+ ERR_FAIL_COND(voxel_gi_data.is_null());
+ voxel_gi_data->set_path(p_path);
+ ResourceSaver::save(voxel_gi_data, p_path, ResourceSaver::FLAG_CHANGE_PATH);
}
}
diff --git a/editor/pot_generator.cpp b/editor/pot_generator.cpp
index 9428d29088..ec044ee06e 100644
--- a/editor/pot_generator.cpp
+++ b/editor/pot_generator.cpp
@@ -99,25 +99,27 @@ void POTGenerator::_write_to_pot(const String &p_file) {
return;
}
- String project_name = GLOBAL_GET("application/config/name");
+ String project_name = GLOBAL_GET("application/config/name").operator String().replace("\n", "\\n");
Vector<String> files = GLOBAL_GET("internationalization/locale/translations_pot_files");
String extracted_files = "";
for (int i = 0; i < files.size(); i++) {
- extracted_files += "# " + files[i] + "\n";
+ extracted_files += "# " + files[i].replace("\n", "\\n") + "\n";
}
const String header =
- "# LANGUAGE translation for " + project_name + " for the following files:\n" + extracted_files +
+ "# LANGUAGE translation for " + project_name + " for the following files:\n" +
+ extracted_files +
"#\n"
- "# FIRST AUTHOR < EMAIL @ADDRESS>, YEAR.\n"
+ "# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n"
"#\n"
"#, fuzzy\n"
"msgid \"\"\n"
"msgstr \"\"\n"
"\"Project-Id-Version: " +
- project_name + "\\n\"\n"
- "\"MIME-Version: 1.0\\n\"\n"
- "\"Content-Type: text/plain; charset=UTF-8\\n\"\n"
- "\"Content-Transfer-Encoding: 8-bit\\n\"\n";
+ project_name +
+ "\\n\"\n"
+ "\"MIME-Version: 1.0\\n\"\n"
+ "\"Content-Type: text/plain; charset=UTF-8\\n\"\n"
+ "\"Content-Transfer-Encoding: 8-bit\\n\"\n";
file->store_string(header);
@@ -134,12 +136,12 @@ void POTGenerator::_write_to_pot(const String &p_file) {
// Write file locations.
for (const String &E : locations) {
- file->store_line("#: " + E.trim_prefix("res://"));
+ file->store_line("#: " + E.trim_prefix("res://").replace("\n", "\\n"));
}
// Write context.
if (!context.is_empty()) {
- file->store_line("msgctxt \"" + context + "\"");
+ file->store_line("msgctxt " + context.c_escape().quote());
}
// Write msgid.
@@ -158,30 +160,34 @@ void POTGenerator::_write_to_pot(const String &p_file) {
}
void POTGenerator::_write_msgid(Ref<FileAccess> r_file, const String &p_id, bool p_plural) {
- // Split \\n and \n.
- Vector<String> msg_lines;
- Vector<String> temp = p_id.split("\\n");
- for (int i = 0; i < temp.size(); i++) {
- msg_lines.append_array(temp[i].split("\n"));
- }
-
- // Add \n.
- for (int i = 0; i < msg_lines.size() - 1; i++) {
- msg_lines.set(i, msg_lines[i] + "\\n");
- }
-
if (p_plural) {
r_file->store_string("msgid_plural ");
} else {
r_file->store_string("msgid ");
}
- if (msg_lines.size() > 1) {
+ if (p_id.is_empty()) {
+ r_file->store_line("\"\"");
+ return;
+ }
+
+ const Vector<String> lines = p_id.split("\n");
+ const String &last_line = lines[lines.size() - 1]; // `lines` cannot be empty.
+ int pot_line_count = lines.size();
+ if (last_line.is_empty()) {
+ pot_line_count--;
+ }
+
+ if (pot_line_count > 1) {
r_file->store_line("\"\"");
}
- for (int i = 0; i < msg_lines.size(); i++) {
- r_file->store_line("\"" + msg_lines[i] + "\"");
+ for (int i = 0; i < lines.size() - 1; i++) {
+ r_file->store_line((lines[i] + "\n").c_escape().quote());
+ }
+
+ if (!last_line.is_empty()) {
+ r_file->store_line(last_line.c_escape().quote());
}
}
diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp
index 9008b6a600..b2994f3065 100644
--- a/editor/project_converter_3_to_4.cpp
+++ b/editor/project_converter_3_to_4.cpp
@@ -336,7 +336,7 @@ bool ProjectConverter3To4::convert() {
// Checking if folder contains valid Godot 3 project.
// Project should not be converted more than once.
{
- String converter_text = "; Project was converted by built-in tool to Godot 4.0";
+ String converter_text = "; Project was converted by built-in tool to Godot 4";
ERR_FAIL_COND_V_MSG(!FileAccess::exists("project.godot"), false, "Current working directory doesn't contain a \"project.godot\" file for a Godot 3 project.");
@@ -536,7 +536,7 @@ bool ProjectConverter3To4::validate_conversion() {
// Checking if folder contains valid Godot 3 project.
// Project should not be converted more than once.
{
- String conventer_text = "; Project was converted by built-in tool to Godot 4.0";
+ String conventer_text = "; Project was converted by built-in tool to Godot 4";
ERR_FAIL_COND_V_MSG(!FileAccess::exists("project.godot"), false, "Current directory doesn't contain any Godot 3 project");
@@ -857,16 +857,16 @@ bool ProjectConverter3To4::test_conversion(RegExContainer &reg_container) {
valid = valid && test_conversion_gdscript_builtin("\tif OS.get_borderless_window(): pass", "\tif get_window().borderless: pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tOS.set_borderless_window(Settings.borderless)", "\tget_window().borderless = (Settings.borderless)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("\tvar aa = roman(r.move_and_slide( a, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("\tmove_and_slide( a, b, c, d, e, f ) # Roman", "\tset_velocity(a)\n\tset_up_direction(b)\n\tset_floor_stop_on_slope_enabled(c)\n\tset_max_slides(d)\n\tset_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tmove_and_slide() # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("\tvar aa = roman(r.move_and_slide_with_snap( a, g, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\t# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `g`\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("\tmove_and_slide_with_snap( a, g, b, c, d, e, f ) # Roman", "\tset_velocity(a)\n\t# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `g`\n\tset_up_direction(b)\n\tset_floor_stop_on_slope_enabled(c)\n\tset_max_slides(d)\n\tset_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tmove_and_slide() # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("\tvar aa = roman(r.move_and_slide( a, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("\tmove_and_slide( a, b, c, d, e, f ) # Roman", "\tset_velocity(a)\n\tset_up_direction(b)\n\tset_floor_stop_on_slope_enabled(c)\n\tset_max_slides(d)\n\tset_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tmove_and_slide() # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("\tvar aa = roman(r.move_and_slide_with_snap( a, g, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\t# TODOConverter3To4 looks that snap in Godot 4 is float, not vector like in Godot 3 - previous value `g`\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("\tmove_and_slide_with_snap( a, g, b, c, d, e, f ) # Roman", "\tset_velocity(a)\n\t# TODOConverter3To4 looks that snap in Godot 4 is float, not vector like in Godot 3 - previous value `g`\n\tset_up_direction(b)\n\tset_floor_stop_on_slope_enabled(c)\n\tset_max_slides(d)\n\tset_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tmove_and_slide() # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("remove_and_slide(a,b,c,d,e,f)", "remove_and_slide(a,b,c,d,e,f)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("list_dir_begin( a , b )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("list_dir_begin( a )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("list_dir_begin( )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("list_dir_begin( a , b )", "list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("list_dir_begin( a )", "list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("list_dir_begin( )", "list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("sort_custom( a , b )", "sort_custom(Callable(a, b))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
@@ -874,9 +874,9 @@ bool ProjectConverter3To4::test_conversion(RegExContainer &reg_container) {
valid = valid && test_conversion_gdscript_builtin("draw_line(1, 2, 3, 4, 5)", "draw_line(1, 2, 3, 4)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("\timage.lock()", "\tfalse # image.lock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("\timage.unlock()", "\tfalse # image.unlock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("\troman.image.unlock()", "\tfalse # roman.image.unlock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("\timage.lock()", "\tfalse # image.lock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("\timage.unlock()", "\tfalse # image.unlock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("\troman.image.unlock()", "\tfalse # roman.image.unlock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tmtx.lock()", "\tmtx.lock()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\tmutex.unlock()", "\tmutex.unlock()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
@@ -998,7 +998,7 @@ bool ProjectConverter3To4::test_conversion(RegExContainer &reg_container) {
valid = valid && test_conversion_gdscript_builtin("apply_force(position, impulse)", "apply_force(impulse, position)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("apply_impulse(position, impulse)", "apply_impulse(impulse, position)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid && test_conversion_gdscript_builtin("draw_rect(a,b,c,d,e).abc", "draw_rect(a, b, c, d).abc# e) TODOGODOT4 Antialiasing argument is missing", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid && test_conversion_gdscript_builtin("draw_rect(a,b,c,d,e).abc", "draw_rect(a, b, c, d).abc# e) TODOConverter3To4 Antialiasing argument is missing", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("get_focus_owner()", "get_viewport().gui_get_focus_owner()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("button.pressed = 1", "button.button_pressed = 1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("button.pressed=1", "button.button_pressed=1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
@@ -1149,13 +1149,13 @@ bool ProjectConverter3To4::test_array_names() {
// Light2D, Texture, Viewport are special classes(probably virtual ones).
if (ClassDB::class_exists(StringName(old_class)) && old_class != "Light2D" && old_class != "Texture" && old_class != "Viewport") {
- ERR_PRINT(vformat("Class \"%s\" exists in Godot 4.0, so it cannot be renamed to something else.", old_class));
+ ERR_PRINT(vformat("Class \"%s\" exists in Godot 4, so it cannot be renamed to something else.", old_class));
valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI.
}
// Callable is special class, to which normal classes may be renamed.
if (!ClassDB::class_exists(StringName(new_class)) && new_class != "Callable") {
- ERR_PRINT(vformat("Class \"%s\" does not exist in Godot 4.0, so it cannot be used in the conversion.", old_class));
+ ERR_PRINT(vformat("Class \"%s\" does not exist in Godot 4, so it cannot be used in the conversion.", old_class));
valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI.
}
}
@@ -1630,8 +1630,8 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// With longer lines, doing so can sometimes be significantly faster.
if ((line.contains(".lock") || line.contains(".unlock")) && !line.contains("mtx") && !line.contains("mutex") && !line.contains("Mutex")) {
- line = reg_container.reg_image_lock.sub(line, "false # $1.lock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", true);
- line = reg_container.reg_image_unlock.sub(line, "false # $1.unlock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", true);
+ line = reg_container.reg_image_lock.sub(line, "false # $1.lock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", true);
+ line = reg_container.reg_image_unlock.sub(line, "false # $1.unlock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", true);
}
// PackedStringArray(req_godot).join('.') -> '.'.join(PackedStringArray(req_godot)) PoolStringArray
@@ -1832,7 +1832,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// infiinite_interia
if (parts.size() >= 6) {
- line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[5] + "`\n";
+ line_new += starting_space + "# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `" + parts[5] + "`\n";
}
line_new += starting_space + base_obj + "move_and_slide()";
@@ -1863,7 +1863,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// snap
if (parts.size() >= 2) {
- line_new += starting_space + "# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `" + parts[1] + "`\n";
+ line_new += starting_space + "# TODOConverter3To4 looks that snap in Godot 4 is float, not vector like in Godot 3 - previous value `" + parts[1] + "`\n";
}
// up_direction
@@ -1888,7 +1888,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// infiinite_interia
if (parts.size() >= 7) {
- line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[6] + "`\n";
+ line_new += starting_space + "# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `" + parts[6] + "`\n";
}
line_new += starting_space + base_obj + "move_and_slide()";
@@ -1919,7 +1919,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
int start = line.find("list_dir_begin(");
int end = get_end_parenthesis(line.substr(start)) + 1;
if (end > -1) {
- line = line.substr(0, start) + "list_dir_begin() " + line.substr(end + start) + "# TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547";
+ line = line.substr(0, start) + "list_dir_begin() " + line.substr(end + start) + "# TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547";
}
}
@@ -2203,14 +2203,14 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
}
}
}
- // draw_rect(a,b,c,d,e) -> draw_rect(a,b,c,d)#e) TODOGODOT4 Antialiasing argument is missing
+ // draw_rect(a,b,c,d,e) -> draw_rect(a,b,c,d)#e) TODOConverter3To4 Antialiasing argument is missing
if (contains_function_call(line, "draw_rect(")) {
int start = line.find("draw_rect(");
int end = get_end_parenthesis(line.substr(start)) + 1;
if (end > -1) {
Vector<String> parts = parse_arguments(line.substr(start, end));
if (parts.size() == 5) {
- line = line.substr(0, start) + "draw_rect(" + parts[0] + ", " + parts[1] + ", " + parts[2] + ", " + parts[3] + ")" + line.substr(end + start) + "# " + parts[4] + ") TODOGODOT4 Antialiasing argument is missing";
+ line = line.substr(0, start) + "draw_rect(" + parts[0] + ", " + parts[1] + ", " + parts[2] + ", " + parts[3] + ")" + line.substr(end + start) + "# " + parts[4] + ") TODOConverter3To4 Antialiasing argument is missing";
}
}
}
@@ -2241,9 +2241,10 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
// rotating = true -> ignore_rotation = false # reversed "rotating" for Camera2D
if (contains_function_call(line, "rotating")) {
- int start = line.find("rotating");
+ String function_name = "rotating";
+ int start = line.find(function_name);
bool foundNextEqual = false;
- String line_to_check = line.substr(start + String("rotating").length());
+ String line_to_check = line.substr(start + function_name.length());
String assigned_value;
for (int current_index = 0; line_to_check.length() > current_index; current_index++) {
char32_t chr = line_to_check.get(current_index);
@@ -2251,14 +2252,14 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
continue;
} else if (chr == '=') {
foundNextEqual = true;
- assigned_value = line.right(current_index).strip_edges();
+ assigned_value = line.substr(start + function_name.length() + current_index + 1).strip_edges();
assigned_value = assigned_value == "true" ? "false" : "true";
} else {
break;
}
}
if (foundNextEqual) {
- line = line.substr(0, start) + "ignore_rotation =" + assigned_value + " # reversed \"rotating\" for Camera2D";
+ line = line.substr(0, start) + "ignore_rotation = " + assigned_value + " # reversed \"rotating\" for Camera2D";
}
}
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 98ce9baaf3..7d2950f6f6 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"
@@ -1042,10 +1043,25 @@ void ProjectListItemControl::set_project_icon(const Ref<Texture2D> &p_icon) {
project_icon->set_texture(p_icon);
}
-void ProjectListItemControl::set_unsupported_features(const PackedStringArray &p_features) {
+bool _project_feature_looks_like_version(const String &p_feature) {
+ return p_feature.contains(".") && p_feature.substr(0, 3).is_numeric();
+}
+
+void ProjectListItemControl::set_unsupported_features(PackedStringArray p_features) {
if (p_features.size() > 0) {
- String unsupported_features_str = String(", ").join(p_features);
- project_unsupported_features->set_tooltip_text(TTR("The project uses features unsupported by the current build:") + "\n" + unsupported_features_str);
+ String tooltip_text = "";
+ for (int i = 0; i < p_features.size(); i++) {
+ if (_project_feature_looks_like_version(p_features[i])) {
+ tooltip_text += TTR("This project was last edited in a different Godot version: ") + p_features[i] + "\n";
+ p_features.remove_at(i);
+ i--;
+ }
+ }
+ if (p_features.size() > 0) {
+ String unsupported_features_str = String(", ").join(p_features);
+ tooltip_text += TTR("This project uses features unsupported by the current build:") + "\n" + unsupported_features_str;
+ }
+ project_unsupported_features->set_tooltip_text(tooltip_text);
project_unsupported_features->show();
} else {
project_unsupported_features->hide();
@@ -1450,7 +1466,7 @@ void ProjectList::_create_project_item_control(int p_index) {
hb->set_project_path(item.path);
hb->set_tooltip_text(item.description);
hb->set_tags(item.tags, this);
- hb->set_unsupported_features(item.unsupported_features);
+ hb->set_unsupported_features(item.unsupported_features.duplicate());
hb->set_is_favorite(item.favorite);
hb->set_is_missing(item.missing);
@@ -1961,10 +1977,10 @@ void ProjectManager::_notification(int p_what) {
real_t size = get_size().x / EDSCALE;
// Adjust names of tabs to fit the new size.
if (size < 650) {
- local_projects_hb->set_name(TTR("Local"));
+ local_projects_vb->set_name(TTR("Local"));
asset_library->set_name(TTR("Asset Library"));
} else {
- local_projects_hb->set_name(TTR("Local Projects"));
+ local_projects_vb->set_name(TTR("Local Projects"));
asset_library->set_name(TTR("Asset Library Projects"));
}
}
@@ -2297,8 +2313,8 @@ void ProjectManager::_open_selected_projects_ask() {
warning_message += TTR("Warning: This project uses C#, but this build of Godot does not have\nthe Mono module. If you proceed you will not be able to use any C# scripts.\n\n");
unsupported_features.remove_at(i);
i--;
- } else if (feature.substr(0, 3).is_numeric()) {
- warning_message += vformat(TTR("Warning: This project was built in Godot %s.\nOpening will upgrade or downgrade the project to Godot %s.\n\n"), Variant(feature), Variant(VERSION_BRANCH));
+ } else if (_project_feature_looks_like_version(feature)) {
+ warning_message += vformat(TTR("Warning: This project was last edited in Godot %s. Opening will change it to Godot %s.\n\n"), Variant(feature), Variant(VERSION_BRANCH));
unsupported_features.remove_at(i);
i--;
}
@@ -2829,19 +2845,39 @@ ProjectManager::ProjectManager() {
tabs->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
tabs->connect("tab_changed", callable_mp(this, &ProjectManager::_on_tab_changed));
- local_projects_hb = memnew(HBoxContainer);
- local_projects_hb->set_name(TTR("Local Projects"));
- tabs->add_child(local_projects_hb);
+ local_projects_vb = memnew(VBoxContainer);
+ local_projects_vb->set_name(TTR("Local Projects"));
+ tabs->add_child(local_projects_vb);
{
- // Projects + search bar
- VBoxContainer *search_tree_vb = memnew(VBoxContainer);
- local_projects_hb->add_child(search_tree_vb);
- search_tree_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-
+ // A bar at top with buttons and options.
HBoxContainer *hb = memnew(HBoxContainer);
hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- search_tree_vb->add_child(hb);
+ local_projects_vb->add_child(hb);
+
+ create_btn = memnew(Button);
+ create_btn->set_text(TTR("New"));
+ create_btn->set_shortcut(ED_SHORTCUT("project_manager/new_project", TTR("New Project"), KeyModifierMask::CMD_OR_CTRL | Key::N));
+ create_btn->connect("pressed", callable_mp(this, &ProjectManager::_new_project));
+ hb->add_child(create_btn);
+
+ import_btn = memnew(Button);
+ import_btn->set_text(TTR("Import"));
+ import_btn->set_shortcut(ED_SHORTCUT("project_manager/import_project", TTR("Import Project"), KeyModifierMask::CMD_OR_CTRL | Key::I));
+ import_btn->connect("pressed", callable_mp(this, &ProjectManager::_import_project));
+ hb->add_child(import_btn);
+
+ scan_btn = memnew(Button);
+ scan_btn->set_text(TTR("Scan"));
+ scan_btn->set_shortcut(ED_SHORTCUT("project_manager/scan_projects", TTR("Scan Projects"), KeyModifierMask::CMD_OR_CTRL | Key::S));
+ scan_btn->connect("pressed", callable_mp(this, &ProjectManager::_scan_projects));
+ hb->add_child(scan_btn);
+
+ loading_label = memnew(Label(TTR("Loading, please wait...")));
+ loading_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ hb->add_child(loading_label);
+ // The loading label is shown later.
+ loading_label->hide();
search_box = memnew(LineEdit);
search_box->set_placeholder(TTR("Filter Projects"));
@@ -2851,12 +2887,6 @@ ProjectManager::ProjectManager() {
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hb->add_child(search_box);
- loading_label = memnew(Label(TTR("Loading, please wait...")));
- loading_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- hb->add_child(loading_label);
- // The loading label is shown later.
- loading_label->hide();
-
Label *sort_label = memnew(Label);
sort_label->set_text(TTR("Sort:"));
hb->add_child(sort_label);
@@ -2864,6 +2894,7 @@ ProjectManager::ProjectManager() {
filter_option = memnew(OptionButton);
filter_option->set_clip_text(true);
filter_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ filter_option->set_stretch_ratio(0.3);
filter_option->connect("item_selected", callable_mp(this, &ProjectManager::_on_order_option_changed));
hb->add_child(filter_option);
@@ -2876,41 +2907,28 @@ ProjectManager::ProjectManager() {
for (int i = 0; i < sort_filter_titles.size(); i++) {
filter_option->add_item(sort_filter_titles[i]);
}
+ }
+
+ {
+ // A container for the project list and for the side bar with buttons.
+ HBoxContainer *search_tree_hb = memnew(HBoxContainer);
+ local_projects_vb->add_child(search_tree_hb);
+ search_tree_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
search_panel = memnew(PanelContainer);
- search_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- search_tree_vb->add_child(search_panel);
+ search_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ search_tree_hb->add_child(search_panel);
_project_list = memnew(ProjectList);
_project_list->connect(ProjectList::SIGNAL_SELECTION_CHANGED, callable_mp(this, &ProjectManager::_update_project_buttons));
_project_list->connect(ProjectList::SIGNAL_PROJECT_ASK_OPEN, callable_mp(this, &ProjectManager::_open_selected_projects_ask));
_project_list->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
search_panel->add_child(_project_list);
- }
- {
- // Project tab side bar
+ // The side bar with the edit, run, rename, etc. buttons.
VBoxContainer *tree_vb = memnew(VBoxContainer);
tree_vb->set_custom_minimum_size(Size2(120, 120));
- local_projects_hb->add_child(tree_vb);
-
- create_btn = memnew(Button);
- create_btn->set_text(TTR("New Project"));
- create_btn->set_shortcut(ED_SHORTCUT("project_manager/new_project", TTR("New Project"), KeyModifierMask::CMD_OR_CTRL | Key::N));
- create_btn->connect("pressed", callable_mp(this, &ProjectManager::_new_project));
- tree_vb->add_child(create_btn);
-
- import_btn = memnew(Button);
- import_btn->set_text(TTR("Import"));
- import_btn->set_shortcut(ED_SHORTCUT("project_manager/import_project", TTR("Import Project"), KeyModifierMask::CMD_OR_CTRL | Key::I));
- import_btn->connect("pressed", callable_mp(this, &ProjectManager::_import_project));
- tree_vb->add_child(import_btn);
-
- scan_btn = memnew(Button);
- scan_btn->set_text(TTR("Scan"));
- scan_btn->set_shortcut(ED_SHORTCUT("project_manager/scan_projects", TTR("Scan Projects"), KeyModifierMask::CMD_OR_CTRL | Key::S));
- scan_btn->connect("pressed", callable_mp(this, &ProjectManager::_scan_projects));
- tree_vb->add_child(scan_btn);
+ search_tree_hb->add_child(tree_vb);
tree_vb->add_child(memnew(HSeparator));
@@ -3098,7 +3116,7 @@ ProjectManager::ProjectManager() {
ask_full_convert_dialog = memnew(ConfirmationDialog);
ask_full_convert_dialog->set_autowrap(true);
- ask_full_convert_dialog->set_text(TTR("This option will perform full project conversion, updating scenes, resources and scripts from Godot 3.x to work in Godot 4.0.\n\nNote that this is a best-effort conversion, i.e. it makes upgrading the project easier, but it will not open out-of-the-box and will still require manual adjustments.\n\nIMPORTANT: Make sure to backup your project before converting, as this operation makes it impossible to open it in older versions of Godot."));
+ ask_full_convert_dialog->set_text(TTR("This option will perform full project conversion, updating scenes, resources and scripts from Godot 3 to work in Godot 4.\n\nNote that this is a best-effort conversion, i.e. it makes upgrading the project easier, but it will not open out-of-the-box and will still require manual adjustments.\n\nIMPORTANT: Make sure to backup your project before converting, as this operation makes it impossible to open it in older versions of Godot."));
ask_full_convert_dialog->connect("confirmed", callable_mp(this, &ProjectManager::_perform_full_project_conversion));
add_child(ask_full_convert_dialog);
diff --git a/editor/project_manager.h b/editor/project_manager.h
index bd82fd0578..46c465f24d 100644
--- a/editor/project_manager.h
+++ b/editor/project_manager.h
@@ -166,7 +166,7 @@ public:
void set_project_path(const String &p_path);
void set_tags(const PackedStringArray &p_tags, ProjectList *p_parent_list);
void set_project_icon(const Ref<Texture2D> &p_icon);
- void set_unsupported_features(const PackedStringArray &p_features);
+ void set_unsupported_features(PackedStringArray p_features);
bool should_load_project_icon() const;
void set_selected(bool p_selected);
@@ -349,7 +349,7 @@ class ProjectManager : public Control {
Button *erase_missing_btn = nullptr;
Button *about_btn = nullptr;
- HBoxContainer *local_projects_hb = nullptr;
+ VBoxContainer *local_projects_vb = nullptr;
EditorAssetLibrary *asset_library = nullptr;
Ref<StyleBox> tag_stylebox;
diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp
index b674ce5967..636bb01557 100644
--- a/editor/register_editor_types.cpp
+++ b/editor/register_editor_types.cpp
@@ -50,7 +50,19 @@
#include "editor/gui/editor_file_dialog.h"
#include "editor/gui/editor_spin_slider.h"
#include "editor/import/editor_import_plugin.h"
+#include "editor/import/resource_importer_bitmask.h"
+#include "editor/import/resource_importer_bmfont.h"
+#include "editor/import/resource_importer_csv_translation.h"
+#include "editor/import/resource_importer_dynamic_font.h"
+#include "editor/import/resource_importer_image.h"
+#include "editor/import/resource_importer_imagefont.h"
+#include "editor/import/resource_importer_layered_texture.h"
+#include "editor/import/resource_importer_obj.h"
#include "editor/import/resource_importer_scene.h"
+#include "editor/import/resource_importer_shader_file.h"
+#include "editor/import/resource_importer_texture.h"
+#include "editor/import/resource_importer_texture_atlas.h"
+#include "editor/import/resource_importer_wav.h"
#include "editor/plugins/animation_tree_editor_plugin.h"
#include "editor/plugins/audio_stream_editor_plugin.h"
#include "editor/plugins/audio_stream_randomizer_editor_plugin.h"
@@ -168,6 +180,21 @@ void register_editor_types() {
GDREGISTER_CLASS(EditorDebuggerPlugin);
GDREGISTER_ABSTRACT_CLASS(EditorDebuggerSession);
+ // Required to document import options in the class reference.
+ GDREGISTER_CLASS(ResourceImporterBitMap);
+ GDREGISTER_CLASS(ResourceImporterBMFont);
+ GDREGISTER_CLASS(ResourceImporterCSVTranslation);
+ GDREGISTER_CLASS(ResourceImporterDynamicFont);
+ GDREGISTER_CLASS(ResourceImporterImage);
+ GDREGISTER_CLASS(ResourceImporterImageFont);
+ GDREGISTER_CLASS(ResourceImporterLayeredTexture);
+ GDREGISTER_CLASS(ResourceImporterOBJ);
+ GDREGISTER_CLASS(ResourceImporterScene);
+ GDREGISTER_CLASS(ResourceImporterShaderFile);
+ GDREGISTER_CLASS(ResourceImporterTexture);
+ GDREGISTER_CLASS(ResourceImporterTextureAtlas);
+ GDREGISTER_CLASS(ResourceImporterWAV);
+
// This list is alphabetized, and plugins that depend on Node2D are in their own section below.
EditorPlugins::add_by_type<AnimationTreeEditorPlugin>();
EditorPlugins::add_by_type<AudioStreamEditorPlugin>();
@@ -227,7 +254,8 @@ void register_editor_types() {
EditorPlugins::add_by_type<Cast2DEditorPlugin>();
EditorPlugins::add_by_type<Skeleton2DEditorPlugin>();
EditorPlugins::add_by_type<Sprite2DEditorPlugin>();
- EditorPlugins::add_by_type<TilesEditorPlugin>();
+ EditorPlugins::add_by_type<TileMapEditorPlugin>();
+ EditorPlugins::add_by_type<TileSetEditorPlugin>();
// For correct doc generation.
GLOBAL_DEF("editor/run/main_run_args", "");
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 23272ab284..a4eabf409a 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -863,7 +863,7 @@ ScriptLanguage::ScriptTemplate ScriptCreateDialog::_parse_template(const ScriptL
ScriptLanguage::ScriptTemplate script_template = ScriptLanguage::ScriptTemplate();
script_template.origin = p_origin;
script_template.inherit = p_inherits;
- String space_indent = " ";
+ int space_indent_size = 4;
// Get meta delimiter
String meta_delimiter;
List<String> comment_delimiters;
@@ -884,30 +884,49 @@ ScriptLanguage::ScriptTemplate ScriptCreateDialog::_parse_template(const ScriptL
String line = file->get_line();
if (line.begins_with(meta_prefix)) {
// Store meta information
- line = line.substr(meta_prefix.length(), -1);
- if (line.begins_with("name")) {
- script_template.name = line.substr(5, -1).strip_edges();
- }
- if (line.begins_with("description")) {
- script_template.description = line.substr(12, -1).strip_edges();
- }
- if (line.begins_with("space-indent")) {
- String indent_value = line.substr(17, -1).strip_edges();
+ line = line.substr(meta_prefix.length());
+ if (line.begins_with("name:")) {
+ script_template.name = line.substr(5).strip_edges();
+ } else if (line.begins_with("description:")) {
+ script_template.description = line.substr(12).strip_edges();
+ } else if (line.begins_with("space-indent:")) {
+ String indent_value = line.substr(13).strip_edges();
if (indent_value.is_valid_int()) {
int indent_size = indent_value.to_int();
if (indent_size >= 0) {
- space_indent = String(" ").repeat(indent_size);
+ space_indent_size = indent_size;
+ } else {
+ WARN_PRINT(vformat("Template meta-space-indent need to be a non-negative integer value. Found %s.", indent_value));
}
} else {
- WARN_PRINT(vformat("Template meta-use_space_indent need to be a valid integer value. Found %s.", indent_value));
+ WARN_PRINT(vformat("Template meta-space-indent need to be a valid integer value. Found %s.", indent_value));
}
}
} else {
- // Store script
- if (space_indent != "") {
- line = line.replace(space_indent, "_TS_");
+ // Replace indentation.
+ int i = 0;
+ int space_count = 0;
+ for (; i < line.length(); i++) {
+ if (line[i] == '\t') {
+ if (space_count) {
+ script_template.content += String(" ").repeat(space_count);
+ space_count = 0;
+ }
+ script_template.content += "_TS_";
+ } else if (line[i] == ' ') {
+ space_count++;
+ if (space_count == space_indent_size) {
+ script_template.content += "_TS_";
+ space_count = 0;
+ }
+ } else {
+ break;
+ }
+ }
+ if (space_count) {
+ script_template.content += String(" ").repeat(space_count);
}
- script_template.content += line.replace("\t", "_TS_") + "\n";
+ script_template.content += line.substr(i) + "\n";
}
}
}
diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp
index 3d8a6e368d..607b53718b 100644
--- a/editor/shader_create_dialog.cpp
+++ b/editor/shader_create_dialog.cpp
@@ -164,37 +164,64 @@ void ShaderCreateDialog::_create_new() {
code += vformat("shader_type %s;\n", mode_menu->get_text().to_snake_case());
if (current_template == 0) { // Default template.
- code += "\n";
switch (current_mode) {
case Shader::MODE_SPATIAL:
- code += "void fragment() {\n";
- code += "\t// Place fragment code here.\n";
- code += "}\n";
+ code += R"(
+void vertex() {
+ // Called for every vertex the material is visible on.
+}
+
+void fragment() {
+ // Called for every pixel the material is visible on.
+}
+
+void light() {
+ // Called for every pixel for every light affecting the material.
+}
+)";
break;
case Shader::MODE_CANVAS_ITEM:
- code += "void fragment() {\n";
- code += "\t// Place fragment code here.\n";
- code += "}\n";
+ code += R"(
+void vertex() {
+ // Called for every vertex the material is visible on.
+}
+
+void fragment() {
+ // Called for every pixel the material is visible on.
+}
+
+void light() {
+ // Called for every pixel for every light affecting the CanvasItem.
+}
+)";
break;
case Shader::MODE_PARTICLES:
- code += "void start() {\n";
- code += "\t// Place start code here.\n";
- code += "}\n";
- code += "\n";
- code += "void process() {\n";
- code += "\t// Place process code here.\n";
- code += "}\n";
+ code += R"(
+void start() {
+ // Called when a particle is spawned.
+}
+
+void process() {
+ // Called every frame on existing particles (according to the Fixed FPS property).
+}
+)";
break;
case Shader::MODE_SKY:
- code += "void sky() {\n";
- code += "\t// Place sky code here.\n";
- code += "}\n";
+ code += R"(
+void sky() {
+ // Called for every visible pixel in the sky background, as well as all pixels
+ // in the radiance cubemap.
+}
+)";
break;
case Shader::MODE_FOG:
- code += "void fog() {\n";
- code += "\t// Place fog code here.\n";
- code += "}\n";
- break;
+ code += R"(
+void fog() {
+ // Called once for every froxel that is touched by an axis-aligned bounding box
+ // of the associated FogVolume. This means that froxels that just barely touch
+ // a given FogVolume will still be used.
+}
+)";
}
}
text_shader->set_code(code.as_string());
diff --git a/editor/window_wrapper.cpp b/editor/window_wrapper.cpp
index 3a8dbf017f..91d5aa8860 100644
--- a/editor/window_wrapper.cpp
+++ b/editor/window_wrapper.cpp
@@ -313,7 +313,7 @@ void WindowWrapper::set_margins_enabled(bool p_enabled) {
}
WindowWrapper::WindowWrapper() {
- if (SceneTree::get_singleton()->get_root()->is_embedding_subwindows() || !EDITOR_GET("interface/multi_window/enable")) {
+ if (SceneTree::get_singleton()->get_root()->is_embedding_subwindows() || EDITOR_GET("interface/editor/single_window_mode") || !EDITOR_GET("interface/multi_window/enable")) {
return;
}
diff --git a/main/main.cpp b/main/main.cpp
index 397476ae39..7e2741648d 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -474,6 +474,7 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print("Standalone tools:\n");
OS::get_singleton()->print(" -s, --script <script> Run a script.\n");
+ OS::get_singleton()->print(" --main-loop <main_loop_name> Run a MainLoop specified by its global class name.\n");
OS::get_singleton()->print(" --check-only Only parse for errors and quit (use with --script).\n");
#ifdef TOOLS_ENABLED
OS::get_singleton()->print(" --export-release <preset> <path> Export the project in release mode using the given preset and output path. The preset name should match one defined in export_presets.cfg.\n");
@@ -2602,6 +2603,7 @@ bool Main::start() {
String positional_arg;
String game_path;
String script;
+ String main_loop_type;
bool check_only = false;
#ifdef TOOLS_ENABLED
@@ -2665,6 +2667,8 @@ bool Main::start() {
bool parsed_pair = true;
if (args[i] == "-s" || args[i] == "--script") {
script = args[i + 1];
+ } else if (args[i] == "--main-loop") {
+ main_loop_type = args[i + 1];
#ifdef TOOLS_ENABLED
} else if (args[i] == "--doctool") {
doc_tool_path = args[i + 1];
@@ -2706,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
@@ -2884,7 +2891,9 @@ bool Main::start() {
if (editor) {
main_loop = memnew(SceneTree);
}
- String main_loop_type = GLOBAL_GET("application/run/main_loop_type");
+ if (main_loop_type.is_empty()) {
+ main_loop_type = GLOBAL_GET("application/run/main_loop_type");
+ }
if (!script.is_empty()) {
Ref<Script> script_res = ResourceLoader::load(script);
@@ -2911,7 +2920,7 @@ bool Main::start() {
ERR_FAIL_V_MSG(false, vformat("Can't load the script \"%s\" as it doesn't inherit from SceneTree or MainLoop.", script));
}
- script_loop->set_initialize_script(script_res);
+ script_loop->set_script(script_res);
main_loop = script_loop;
} else {
return false;
@@ -2934,7 +2943,7 @@ bool Main::start() {
OS::get_singleton()->alert("Error: Invalid MainLoop script base type: " + script_base);
ERR_FAIL_V_MSG(false, vformat("The global class %s does not inherit from SceneTree or MainLoop.", main_loop_type));
}
- script_loop->set_initialize_script(script_res);
+ script_loop->set_script(script_res);
main_loop = script_loop;
}
}
@@ -2959,6 +2968,8 @@ bool Main::start() {
}
}
+ OS::get_singleton()->set_main_loop(main_loop);
+
SceneTree *sml = Object::cast_to<SceneTree>(main_loop);
if (sml) {
#ifdef DEBUG_ENABLED
@@ -3278,8 +3289,6 @@ bool Main::start() {
DisplayServer::get_singleton()->set_icon(icon);
}
- OS::get_singleton()->set_main_loop(main_loop);
-
if (movie_writer) {
movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, Engine::get_singleton()->get_write_movie_path());
}
diff --git a/misc/extension_api_validation/4.0-stable.expected b/misc/extension_api_validation/4.0-stable.expected
index c9ebd83ba3..380c1d5193 100644
--- a/misc/extension_api_validation/4.0-stable.expected
+++ b/misc/extension_api_validation/4.0-stable.expected
@@ -358,3 +358,24 @@ Validate extension JSON: Error: Hash changed for 'classes/EditorUndoRedoManager/
Validate extension JSON: Error: Hash changed for 'classes/UndoRedo/methods/create_action', from 0AEC1BFC to E87757EB. This means that the function has changed and no compatibility function was provided.
Added a optional parameters with default values. No adjustments should be necessary.
+
+GH-79911
+--------
+Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_RASTER': value changed value in new API, from 1.0 to 9.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_ALL_BARRIERS': value changed value in new API, from 7.0 to 32767.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_NO_BARRIER': value changed value in new API, from 8.0 to 32768.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_update/arguments/3': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_copy/arguments/9': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_clear/arguments/6': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_resolve_multisample/arguments/2': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/buffer_update/arguments/4': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/buffer_clear/arguments/3': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_end/arguments/0': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/draw_list_end', from 19365687 to E9B4FA8E. This means that the function has changed and no compatibility function was provided.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/compute_list_end/arguments/0': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/compute_list_end', from 19365687 to E9B4FA8E. This means that the function has changed and no compatibility function was provided.
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/barrier/arguments/0': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/barrier/arguments/1': default_value changed value in new API, from "7" to "32767".
+Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/barrier', from 0FE50041 to DD9E8DAB. This means that the function has changed and no compatibility function was provided.
+
+Raster barrier was split into vertex and fragment barriers for use in mobile renderer.
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/image_loader_dds.cpp b/modules/dds/image_loader_dds.cpp
new file mode 100644
index 0000000000..42c8120595
--- /dev/null
+++ b/modules/dds/image_loader_dds.cpp
@@ -0,0 +1,422 @@
+/**************************************************************************/
+/* image_loader_dds.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_loader_dds.h"
+
+#include "core/os/os.h"
+
+#include "core/io/file_access.h"
+#include "core/io/file_access_memory.h"
+
+#include <string.h>
+
+#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])))
+
+// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
+
+enum {
+ DDS_MAGIC = 0x20534444,
+ DDSD_PITCH = 0x00000008,
+ DDSD_LINEARSIZE = 0x00080000,
+ DDSD_MIPMAPCOUNT = 0x00020000,
+ DDPF_FOURCC = 0x00000004,
+ DDPF_ALPHAPIXELS = 0x00000001,
+ DDPF_INDEXED = 0x00000020,
+ DDPF_RGB = 0x00000040,
+};
+
+enum DDSFormat {
+ DDS_DXT1,
+ DDS_DXT3,
+ DDS_DXT5,
+ DDS_ATI1,
+ DDS_ATI2,
+ DDS_A2XY,
+ DDS_BGRA8,
+ DDS_BGR8,
+ DDS_RGBA8, //flipped in dds
+ DDS_RGB8, //flipped in dds
+ DDS_BGR5A1,
+ DDS_BGR565,
+ DDS_BGR10A2,
+ DDS_INDEXED,
+ DDS_LUMINANCE,
+ DDS_LUMINANCE_ALPHA,
+ DDS_MAX
+};
+
+struct DDSFormatInfo {
+ const char *name = nullptr;
+ bool compressed = false;
+ bool palette = false;
+ uint32_t divisor = 0;
+ uint32_t block_size = 0;
+ Image::Format format = Image::Format::FORMAT_BPTC_RGBA;
+};
+
+static const DDSFormatInfo dds_format_info[DDS_MAX] = {
+ { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 },
+ { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 },
+ { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 },
+ { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R },
+ { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG },
+ { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG },
+ { "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
+ { "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 },
+ { "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
+ { "RGB8", false, false, 1, 3, Image::FORMAT_RGB8 },
+ { "BGR5A1", false, false, 1, 2, Image::FORMAT_RGBA8 },
+ { "BGR565", false, false, 1, 2, Image::FORMAT_RGB8 },
+ { "BGR10A2", false, false, 1, 4, Image::FORMAT_RGBA8 },
+ { "GRAYSCALE", false, false, 1, 1, Image::FORMAT_L8 },
+ { "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 }
+};
+
+static Ref<Image> _dds_mem_loader_func(const uint8_t *p_buffer, int p_buffer_len) {
+ Ref<FileAccessMemory> memfile;
+ memfile.instantiate();
+ Error open_memfile_error = memfile->open_custom(p_buffer, p_buffer_len);
+ ERR_FAIL_COND_V_MSG(open_memfile_error, Ref<Image>(), "Could not create memfile for DDS image buffer.");
+
+ Ref<Image> img;
+ img.instantiate();
+ Error load_error = ImageLoaderDDS().load_image(img, memfile, false, 1.0f);
+ ERR_FAIL_COND_V_MSG(load_error, Ref<Image>(), "Failed to load DDS image.");
+ return img;
+}
+
+Error ImageLoaderDDS::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
+ uint32_t magic = f->get_32();
+ uint32_t hsize = f->get_32();
+ uint32_t flags = f->get_32();
+ uint32_t height = f->get_32();
+ uint32_t width = f->get_32();
+ uint32_t pitch = f->get_32();
+ /* uint32_t depth = */ f->get_32();
+ uint32_t mipmaps = f->get_32();
+
+ //skip 11
+ for (int i = 0; i < 11; i++) {
+ f->get_32();
+ }
+
+ //validate
+
+ // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing,
+ // but non-mandatory when reading (as some writers don't set them)...
+ if (magic != DDS_MAGIC || hsize != 124) {
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid or unsupported DDS texture file '" + f->get_path() + "'.");
+ }
+
+ /* uint32_t format_size = */ f->get_32();
+ uint32_t format_flags = f->get_32();
+ uint32_t format_fourcc = f->get_32();
+ uint32_t format_rgb_bits = f->get_32();
+ uint32_t format_red_mask = f->get_32();
+ uint32_t format_green_mask = f->get_32();
+ uint32_t format_blue_mask = f->get_32();
+ uint32_t format_alpha_mask = f->get_32();
+
+ /* uint32_t caps_1 = */ f->get_32();
+ /* uint32_t caps_2 = */ f->get_32();
+ /* uint32_t caps_ddsx = */ f->get_32();
+
+ //reserved skip
+ f->get_32();
+ f->get_32();
+
+ /*
+ print_line("DDS width: "+itos(width));
+ print_line("DDS height: "+itos(height));
+ print_line("DDS mipmaps: "+itos(mipmaps));
+
+ printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size);
+ printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask);
+ */
+
+ //must avoid this later
+ while (f->get_position() < 128) {
+ f->get_8();
+ }
+
+ DDSFormat dds_format;
+
+ if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) {
+ dds_format = DDS_DXT1;
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) {
+ dds_format = DDS_DXT3;
+
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) {
+ dds_format = DDS_DXT5;
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) {
+ dds_format = DDS_ATI1;
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) {
+ dds_format = DDS_ATI2;
+ } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) {
+ dds_format = DDS_A2XY;
+
+ } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) {
+ dds_format = DDS_BGRA8;
+ } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) {
+ dds_format = DDS_BGR8;
+ } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) {
+ dds_format = DDS_RGBA8;
+ } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) {
+ dds_format = DDS_RGB8;
+
+ } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) {
+ dds_format = DDS_BGR5A1;
+ } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) {
+ dds_format = DDS_BGR10A2;
+ } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) {
+ dds_format = DDS_BGR565;
+ } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff) {
+ dds_format = DDS_LUMINANCE;
+ } else if ((format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff && format_alpha_mask == 0xff00) {
+ dds_format = DDS_LUMINANCE_ALPHA;
+ } else if (format_flags & DDPF_INDEXED && format_rgb_bits == 8) {
+ dds_format = DDS_BGR565;
+ } else {
+ //printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n", format_fourcc, format_flags, format_rgb_bits, format_red_mask, format_green_mask, format_blue_mask, format_alpha_mask);
+ ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Unrecognized or unsupported color layout in DDS '" + f->get_path() + "'.");
+ }
+
+ if (!(flags & DDSD_MIPMAPCOUNT)) {
+ mipmaps = 1;
+ }
+
+ Vector<uint8_t> src_data;
+
+ const DDSFormatInfo &info = dds_format_info[dds_format];
+ uint32_t w = width;
+ uint32_t h = height;
+
+ if (info.compressed) {
+ //compressed bc
+
+ uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size;
+ ERR_FAIL_COND_V(size != pitch, ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), ERR_FILE_CORRUPT);
+
+ for (uint32_t i = 1; i < mipmaps; i++) {
+ w = MAX(1u, w >> 1);
+ h = MAX(1u, h >> 1);
+ uint32_t bsize = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size;
+ //printf("%i x %i - block: %i\n",w,h,bsize);
+ size += bsize;
+ }
+
+ src_data.resize(size);
+ uint8_t *wb = src_data.ptrw();
+ f->get_buffer(wb, size);
+
+ } else if (info.palette) {
+ //indexed
+ ERR_FAIL_COND_V(!(flags & DDSD_PITCH), ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(format_rgb_bits != 8, ERR_FILE_CORRUPT);
+
+ uint32_t size = pitch * height;
+ ERR_FAIL_COND_V(size != width * height * info.block_size, ERR_FILE_CORRUPT);
+
+ uint8_t palette[256 * 4];
+ f->get_buffer(palette, 256 * 4);
+
+ int colsize = 3;
+ for (int i = 0; i < 256; i++) {
+ if (palette[i * 4 + 3] < 255) {
+ colsize = 4;
+ }
+ }
+
+ int w2 = width;
+ int h2 = height;
+
+ for (uint32_t i = 1; i < mipmaps; i++) {
+ w2 = (w2 + 1) >> 1;
+ h2 = (h2 + 1) >> 1;
+ size += w2 * h2 * info.block_size;
+ }
+
+ src_data.resize(size + 256 * colsize);
+ uint8_t *wb = src_data.ptrw();
+ f->get_buffer(wb, size);
+
+ for (int i = 0; i < 256; i++) {
+ int dst_ofs = size + i * colsize;
+ int src_ofs = i * 4;
+ wb[dst_ofs + 0] = palette[src_ofs + 2];
+ wb[dst_ofs + 1] = palette[src_ofs + 1];
+ wb[dst_ofs + 2] = palette[src_ofs + 0];
+ if (colsize == 4) {
+ wb[dst_ofs + 3] = palette[src_ofs + 3];
+ }
+ }
+ } else {
+ //uncompressed generic...
+
+ uint32_t size = width * height * info.block_size;
+
+ for (uint32_t i = 1; i < mipmaps; i++) {
+ w = (w + 1) >> 1;
+ h = (h + 1) >> 1;
+ size += w * h * info.block_size;
+ }
+
+ if (dds_format == DDS_BGR565) {
+ size = size * 3 / 2;
+ } else if (dds_format == DDS_BGR5A1) {
+ size = size * 2;
+ }
+
+ src_data.resize(size);
+ uint8_t *wb = src_data.ptrw();
+ f->get_buffer(wb, size);
+
+ switch (dds_format) {
+ case DDS_BGR5A1: {
+ // TO RGBA
+ int colcount = size / 4;
+
+ for (int i = colcount - 1; i >= 0; i--) {
+ int src_ofs = i * 2;
+ int dst_ofs = i * 4;
+
+ uint8_t a = wb[src_ofs + 1] & 0x80;
+ uint8_t b = wb[src_ofs] & 0x1F;
+ uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x3) << 3);
+ uint8_t r = (wb[src_ofs + 1] >> 2) & 0x1F;
+ wb[dst_ofs + 0] = r << 3;
+ wb[dst_ofs + 1] = g << 3;
+ wb[dst_ofs + 2] = b << 3;
+ wb[dst_ofs + 3] = a ? 255 : 0;
+ }
+ } break;
+ case DDS_BGR565: {
+ int colcount = size / 3;
+
+ for (int i = colcount - 1; i >= 0; i--) {
+ int src_ofs = i * 2;
+ int dst_ofs = i * 3;
+
+ uint8_t b = wb[src_ofs] & 0x1F;
+ uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x7) << 3);
+ uint8_t r = wb[src_ofs + 1] >> 3;
+ wb[dst_ofs + 0] = r << 3;
+ wb[dst_ofs + 1] = g << 2;
+ wb[dst_ofs + 2] = b << 3; //b<<3;
+ }
+
+ } break;
+ case DDS_BGR10A2: {
+ // TO RGBA
+ int colcount = size / 4;
+
+ for (int i = colcount - 1; i >= 0; i--) {
+ int ofs = i * 4;
+
+ uint32_t w32 = uint32_t(wb[ofs + 0]) | (uint32_t(wb[ofs + 1]) << 8) | (uint32_t(wb[ofs + 2]) << 16) | (uint32_t(wb[ofs + 3]) << 24);
+
+ uint8_t a = (w32 & 0xc0000000) >> 24;
+ uint8_t r = (w32 & 0x3ff00000) >> 22;
+ uint8_t g = (w32 & 0xffc00) >> 12;
+ uint8_t b = (w32 & 0x3ff) >> 2;
+
+ wb[ofs + 0] = r;
+ wb[ofs + 1] = g;
+ wb[ofs + 2] = b;
+ wb[ofs + 3] = a == 0xc0 ? 255 : a; //0xc0 should be opaque
+ }
+ } break;
+ case DDS_BGRA8: {
+ int colcount = size / 4;
+
+ for (int i = 0; i < colcount; i++) {
+ SWAP(wb[i * 4 + 0], wb[i * 4 + 2]);
+ }
+
+ } break;
+ case DDS_BGR8: {
+ int colcount = size / 3;
+
+ for (int i = 0; i < colcount; i++) {
+ SWAP(wb[i * 3 + 0], wb[i * 3 + 2]);
+ }
+ } break;
+ case DDS_RGBA8: {
+ /* do nothing either
+ int colcount = size/4;
+
+ for(int i=0;i<colcount;i++) {
+ uint8_t r = wb[i*4+1];
+ uint8_t g = wb[i*4+2];
+ uint8_t b = wb[i*4+3];
+ uint8_t a = wb[i*4+0];
+
+ wb[i*4+0]=r;
+ wb[i*4+1]=g;
+ wb[i*4+2]=b;
+ wb[i*4+3]=a;
+ }
+ */
+ } break;
+ case DDS_RGB8: {
+ // do nothing
+ /*
+ int colcount = size/3;
+
+ for(int i=0;i<colcount;i++) {
+ SWAP( wb[i*3+0],wb[i*3+2] );
+ }*/
+ } break;
+ case DDS_LUMINANCE: {
+ // do nothing i guess?
+
+ } break;
+ case DDS_LUMINANCE_ALPHA: {
+ // do nothing i guess?
+
+ } break;
+
+ default: {
+ }
+ }
+ }
+
+ p_image->set_data(width, height, mipmaps - 1, info.format, src_data);
+ return OK;
+}
+
+ImageLoaderDDS::ImageLoaderDDS() {
+ Image::_dds_mem_loader_func = _dds_mem_loader_func;
+}
+
+void ImageLoaderDDS::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("dds");
+}
diff --git a/modules/mono/mono_gd/support/android_support.h b/modules/dds/image_loader_dds.h
index 5be4bac6e1..81cfd43551 100644
--- a/modules/mono/mono_gd/support/android_support.h
+++ b/modules/dds/image_loader_dds.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* android_support.h */
+/* image_loader_dds.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,27 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifndef ANDROID_SUPPORT_H
-#define ANDROID_SUPPORT_H
+#ifndef IMAGE_LOADER_DDS_H
+#define IMAGE_LOADER_DDS_H
-#if defined(ANDROID_ENABLED)
+#include "core/io/image_loader.h"
-#include "core/string/ustring.h"
+class ImageLoaderDDS : public ImageFormatLoader {
+public:
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ ImageLoaderDDS();
+};
-namespace gdmono {
-namespace android {
-namespace support {
-
-String get_app_native_lib_dir();
-
-void initialize();
-void cleanup();
-
-void register_internal_calls();
-} // namespace support
-} // namespace android
-} // namespace gdmono
-
-#endif // ANDROID_ENABLED
-
-#endif // ANDROID_SUPPORT_H
+#endif // IMAGE_LOADER_DDS_H
diff --git a/modules/dds/register_types.cpp b/modules/dds/register_types.cpp
index d336269eb3..b4d406eda9 100644
--- a/modules/dds/register_types.cpp
+++ b/modules/dds/register_types.cpp
@@ -30,9 +30,11 @@
#include "register_types.h"
+#include "image_loader_dds.h"
#include "texture_loader_dds.h"
static Ref<ResourceFormatDDS> resource_loader_dds;
+static Ref<ImageLoaderDDS> image_loader_dds;
void initialize_dds_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
@@ -41,6 +43,9 @@ void initialize_dds_module(ModuleInitializationLevel p_level) {
resource_loader_dds.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_dds);
+
+ image_loader_dds.instantiate();
+ ImageLoader::add_image_format_loader(image_loader_dds);
}
void uninitialize_dds_module(ModuleInitializationLevel p_level) {
@@ -50,4 +55,7 @@ void uninitialize_dds_module(ModuleInitializationLevel p_level) {
ResourceLoader::remove_resource_format_loader(resource_loader_dds);
resource_loader_dds.unref();
+
+ ImageLoader::remove_image_format_loader(image_loader_dds);
+ image_loader_dds.unref();
}
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index e6523e3d09..861cf20cc2 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -29,70 +29,10 @@
/**************************************************************************/
#include "texture_loader_dds.h"
+#include "image_loader_dds.h"
#include "core/io/file_access.h"
-
-#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])))
-
-// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
-
-enum {
- DDS_MAGIC = 0x20534444,
- DDSD_PITCH = 0x00000008,
- DDSD_LINEARSIZE = 0x00080000,
- DDSD_MIPMAPCOUNT = 0x00020000,
- DDPF_FOURCC = 0x00000004,
- DDPF_ALPHAPIXELS = 0x00000001,
- DDPF_INDEXED = 0x00000020,
- DDPF_RGB = 0x00000040,
-};
-
-enum DDSFormat {
- DDS_DXT1,
- DDS_DXT3,
- DDS_DXT5,
- DDS_ATI1,
- DDS_ATI2,
- DDS_A2XY,
- DDS_BGRA8,
- DDS_BGR8,
- DDS_RGBA8, //flipped in dds
- DDS_RGB8, //flipped in dds
- DDS_BGR5A1,
- DDS_BGR565,
- DDS_BGR10A2,
- DDS_INDEXED,
- DDS_LUMINANCE,
- DDS_LUMINANCE_ALPHA,
- DDS_MAX
-};
-
-struct DDSFormatInfo {
- const char *name = nullptr;
- bool compressed = false;
- bool palette = false;
- uint32_t divisor = 0;
- uint32_t block_size = 0;
- Image::Format format = Image::Format::FORMAT_BPTC_RGBA;
-};
-
-static const DDSFormatInfo dds_format_info[DDS_MAX] = {
- { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 },
- { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 },
- { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 },
- { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R },
- { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG },
- { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG },
- { "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
- { "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 },
- { "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
- { "RGB8", false, false, 1, 3, Image::FORMAT_RGB8 },
- { "BGR5A1", false, false, 1, 2, Image::FORMAT_RGBA8 },
- { "BGR565", false, false, 1, 2, Image::FORMAT_RGB8 },
- { "BGR10A2", false, false, 1, 4, Image::FORMAT_RGBA8 },
- { "GRAYSCALE", false, false, 1, 1, Image::FORMAT_L8 },
- { "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 }
-};
+#include "scene/resources/image_texture.h"
Ref<Resource> ResourceFormatDDS::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) {
if (r_error) {
@@ -112,303 +52,12 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Unable to open DDS texture file '" + p_path + "'.");
- uint32_t magic = f->get_32();
- uint32_t hsize = f->get_32();
- uint32_t flags = f->get_32();
- uint32_t height = f->get_32();
- uint32_t width = f->get_32();
- uint32_t pitch = f->get_32();
- /* uint32_t depth = */ f->get_32();
- uint32_t mipmaps = f->get_32();
-
- //skip 11
- for (int i = 0; i < 11; i++) {
- f->get_32();
- }
-
- //validate
-
- // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing,
- // but non-mandatory when reading (as some writers don't set them)...
- if (magic != DDS_MAGIC || hsize != 124) {
- ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid or unsupported DDS texture file '" + p_path + "'.");
- }
-
- /* uint32_t format_size = */ f->get_32();
- uint32_t format_flags = f->get_32();
- uint32_t format_fourcc = f->get_32();
- uint32_t format_rgb_bits = f->get_32();
- uint32_t format_red_mask = f->get_32();
- uint32_t format_green_mask = f->get_32();
- uint32_t format_blue_mask = f->get_32();
- uint32_t format_alpha_mask = f->get_32();
-
- /* uint32_t caps_1 = */ f->get_32();
- /* uint32_t caps_2 = */ f->get_32();
- /* uint32_t caps_ddsx = */ f->get_32();
-
- //reserved skip
- f->get_32();
- f->get_32();
-
- /*
- print_line("DDS width: "+itos(width));
- print_line("DDS height: "+itos(height));
- print_line("DDS mipmaps: "+itos(mipmaps));
-
- printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size);
- printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask);
- */
-
- //must avoid this later
- while (f->get_position() < 128) {
- f->get_8();
- }
-
- DDSFormat dds_format;
-
- if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) {
- dds_format = DDS_DXT1;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) {
- dds_format = DDS_DXT3;
-
- } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) {
- dds_format = DDS_DXT5;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) {
- dds_format = DDS_ATI1;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) {
- dds_format = DDS_ATI2;
- } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) {
- dds_format = DDS_A2XY;
-
- } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) {
- dds_format = DDS_BGRA8;
- } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) {
- dds_format = DDS_BGR8;
- } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) {
- dds_format = DDS_RGBA8;
- } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) {
- dds_format = DDS_RGB8;
-
- } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) {
- dds_format = DDS_BGR5A1;
- } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) {
- dds_format = DDS_BGR10A2;
- } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) {
- dds_format = DDS_BGR565;
- } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff) {
- dds_format = DDS_LUMINANCE;
- } else if ((format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0xff && format_green_mask == 0xff && format_blue_mask == 0xff && format_alpha_mask == 0xff00) {
- dds_format = DDS_LUMINANCE_ALPHA;
- } else if (format_flags & DDPF_INDEXED && format_rgb_bits == 8) {
- dds_format = DDS_BGR565;
- } else {
- //printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n", format_fourcc, format_flags, format_rgb_bits, format_red_mask, format_green_mask, format_blue_mask, format_alpha_mask);
- ERR_FAIL_V_MSG(Ref<Resource>(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'.");
- }
-
- if (!(flags & DDSD_MIPMAPCOUNT)) {
- mipmaps = 1;
- }
-
- Vector<uint8_t> src_data;
-
- const DDSFormatInfo &info = dds_format_info[dds_format];
- uint32_t w = width;
- uint32_t h = height;
-
- if (info.compressed) {
- //compressed bc
-
- uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size;
- ERR_FAIL_COND_V(size != pitch, Ref<Resource>());
- ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), Ref<Resource>());
-
- for (uint32_t i = 1; i < mipmaps; i++) {
- w = MAX(1u, w >> 1);
- h = MAX(1u, h >> 1);
- uint32_t bsize = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size;
- //printf("%i x %i - block: %i\n",w,h,bsize);
- size += bsize;
- }
-
- src_data.resize(size);
- uint8_t *wb = src_data.ptrw();
- f->get_buffer(wb, size);
-
- } else if (info.palette) {
- //indexed
- ERR_FAIL_COND_V(!(flags & DDSD_PITCH), Ref<Resource>());
- ERR_FAIL_COND_V(format_rgb_bits != 8, Ref<Resource>());
-
- uint32_t size = pitch * height;
- ERR_FAIL_COND_V(size != width * height * info.block_size, Ref<Resource>());
-
- uint8_t palette[256 * 4];
- f->get_buffer(palette, 256 * 4);
-
- int colsize = 3;
- for (int i = 0; i < 256; i++) {
- if (palette[i * 4 + 3] < 255) {
- colsize = 4;
- }
- }
-
- int w2 = width;
- int h2 = height;
-
- for (uint32_t i = 1; i < mipmaps; i++) {
- w2 = (w2 + 1) >> 1;
- h2 = (h2 + 1) >> 1;
- size += w2 * h2 * info.block_size;
- }
-
- src_data.resize(size + 256 * colsize);
- uint8_t *wb = src_data.ptrw();
- f->get_buffer(wb, size);
-
- for (int i = 0; i < 256; i++) {
- int dst_ofs = size + i * colsize;
- int src_ofs = i * 4;
- wb[dst_ofs + 0] = palette[src_ofs + 2];
- wb[dst_ofs + 1] = palette[src_ofs + 1];
- wb[dst_ofs + 2] = palette[src_ofs + 0];
- if (colsize == 4) {
- wb[dst_ofs + 3] = palette[src_ofs + 3];
- }
- }
- } else {
- //uncompressed generic...
-
- uint32_t size = width * height * info.block_size;
-
- for (uint32_t i = 1; i < mipmaps; i++) {
- w = (w + 1) >> 1;
- h = (h + 1) >> 1;
- size += w * h * info.block_size;
- }
-
- if (dds_format == DDS_BGR565) {
- size = size * 3 / 2;
- } else if (dds_format == DDS_BGR5A1) {
- size = size * 2;
- }
-
- src_data.resize(size);
- uint8_t *wb = src_data.ptrw();
- f->get_buffer(wb, size);
-
- switch (dds_format) {
- case DDS_BGR5A1: {
- // TO RGBA
- int colcount = size / 4;
-
- for (int i = colcount - 1; i >= 0; i--) {
- int src_ofs = i * 2;
- int dst_ofs = i * 4;
-
- uint8_t a = wb[src_ofs + 1] & 0x80;
- uint8_t b = wb[src_ofs] & 0x1F;
- uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x3) << 3);
- uint8_t r = (wb[src_ofs + 1] >> 2) & 0x1F;
- wb[dst_ofs + 0] = r << 3;
- wb[dst_ofs + 1] = g << 3;
- wb[dst_ofs + 2] = b << 3;
- wb[dst_ofs + 3] = a ? 255 : 0;
- }
- } break;
- case DDS_BGR565: {
- int colcount = size / 3;
-
- for (int i = colcount - 1; i >= 0; i--) {
- int src_ofs = i * 2;
- int dst_ofs = i * 3;
-
- uint8_t b = wb[src_ofs] & 0x1F;
- uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x7) << 3);
- uint8_t r = wb[src_ofs + 1] >> 3;
- wb[dst_ofs + 0] = r << 3;
- wb[dst_ofs + 1] = g << 2;
- wb[dst_ofs + 2] = b << 3; //b<<3;
- }
-
- } break;
- case DDS_BGR10A2: {
- // TO RGBA
- int colcount = size / 4;
-
- for (int i = colcount - 1; i >= 0; i--) {
- int ofs = i * 4;
-
- uint32_t w32 = uint32_t(wb[ofs + 0]) | (uint32_t(wb[ofs + 1]) << 8) | (uint32_t(wb[ofs + 2]) << 16) | (uint32_t(wb[ofs + 3]) << 24);
-
- uint8_t a = (w32 & 0xc0000000) >> 24;
- uint8_t r = (w32 & 0x3ff00000) >> 22;
- uint8_t g = (w32 & 0xffc00) >> 12;
- uint8_t b = (w32 & 0x3ff) >> 2;
-
- wb[ofs + 0] = r;
- wb[ofs + 1] = g;
- wb[ofs + 2] = b;
- wb[ofs + 3] = a == 0xc0 ? 255 : a; //0xc0 should be opaque
- }
- } break;
- case DDS_BGRA8: {
- int colcount = size / 4;
-
- for (int i = 0; i < colcount; i++) {
- SWAP(wb[i * 4 + 0], wb[i * 4 + 2]);
- }
-
- } break;
- case DDS_BGR8: {
- int colcount = size / 3;
-
- for (int i = 0; i < colcount; i++) {
- SWAP(wb[i * 3 + 0], wb[i * 3 + 2]);
- }
- } break;
- case DDS_RGBA8: {
- /* do nothing either
- int colcount = size/4;
-
- for(int i=0;i<colcount;i++) {
- uint8_t r = wb[i*4+1];
- uint8_t g = wb[i*4+2];
- uint8_t b = wb[i*4+3];
- uint8_t a = wb[i*4+0];
-
- wb[i*4+0]=r;
- wb[i*4+1]=g;
- wb[i*4+2]=b;
- wb[i*4+3]=a;
- }
- */
- } break;
- case DDS_RGB8: {
- // do nothing
- /*
- int colcount = size/3;
-
- for(int i=0;i<colcount;i++) {
- SWAP( wb[i*3+0],wb[i*3+2] );
- }*/
- } break;
- case DDS_LUMINANCE: {
- // do nothing i guess?
-
- } break;
- case DDS_LUMINANCE_ALPHA: {
- // do nothing i guess?
-
- } break;
-
- default: {
- }
- }
+ Ref<Image> img = memnew(Image);
+ Error i_error = ImageLoaderDDS().load_image(img, f, false, 1.0);
+ if (r_error) {
+ *r_error = i_error;
}
- Ref<Image> img = memnew(Image(width, height, mipmaps - 1, info.format, src_data));
Ref<ImageTexture> texture = ImageTexture::create_from_image(img);
if (r_error) {
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/enet/enet_packet_peer.cpp b/modules/enet/enet_packet_peer.cpp
index f64704c67d..a131841a07 100644
--- a/modules/enet/enet_packet_peer.cpp
+++ b/modules/enet/enet_packet_peer.cpp
@@ -76,7 +76,7 @@ void ENetPacketPeer::throttle_configure(int p_interval, int p_acceleration, int
void ENetPacketPeer::set_timeout(int p_timeout, int p_timeout_min, int p_timeout_max) {
ERR_FAIL_COND_MSG(peer == nullptr, "Peer not connected");
- ERR_FAIL_COND_MSG(p_timeout > p_timeout_min || p_timeout_min > p_timeout_max, "Timeout limit must be less than minimum timeout, which itself must be less then maximum timeout");
+ ERR_FAIL_COND_MSG(p_timeout > p_timeout_min || p_timeout_min > p_timeout_max, "Timeout limit must be less than minimum timeout, which itself must be less than maximum timeout");
enet_peer_timeout(peer, p_timeout, p_timeout_min, p_timeout_max);
}
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 0c300eade4..dd6b668c45 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -533,6 +533,7 @@
[codeblock]
@export_node_path("Button", "TouchScreenButton") var some_button
[/codeblock]
+ [b]Note:[/b] The type must be a native class or a globally registered script (using the [code]class_name[/code] keyword) that inherits [Node].
</description>
</annotation>
<annotation name="@export_placeholder">
@@ -621,7 +622,7 @@
<description>
Mark the following method for remote procedure calls. See [url=$DOCS_URL/tutorials/networking/high_level_multiplayer.html]High-level multiplayer[/url].
If [param mode] is set as [code]"any_peer"[/code], allows any peer to call this RPC function. Otherwise, only the authority peer is allowed to call it and [param mode] should be kept as [code]"authority"[/code]. When configuring functions as RPCs with [method Node.rpc_config], each of these modes respectively corresponds to the [constant MultiplayerAPI.RPC_MODE_AUTHORITY] and [constant MultiplayerAPI.RPC_MODE_ANY_PEER] RPC modes. See [enum MultiplayerAPI.RPCMode]. If a peer that is not the authority tries to call a function that is only allowed for the authority, the function will not be executed. If the error can be detected locally (when the RPC configuration is consistent between the local and the remote peer), an error message will be displayed on the sender peer. Otherwise, the remote peer will detect the error and print an error there.
- If [param sync] is set as [code]"call_remote"[/code], the function will only be executed on the remote peer, but not locally. To run this function locally too, set [param sync] to [code]"call_local"[/code]. When configuring functions as RPCs with [method Node.rpc_config], this is equivalent to setting `call_local` to `true`.
+ If [param sync] is set as [code]"call_remote"[/code], the function will only be executed on the remote peer, but not locally. To run this function locally too, set [param sync] to [code]"call_local"[/code]. When configuring functions as RPCs with [method Node.rpc_config], this is equivalent to setting [code]call_local[/code] to [code]true[/code].
The [param transfer_mode] accepted values are [code]"unreliable"[/code], [code]"unreliable_ordered"[/code], or [code]"reliable"[/code]. It sets the transfer mode of the underlying [MultiplayerPeer]. See [member MultiplayerPeer.transfer_mode].
The [param transfer_channel] defines the channel of the underlying [MultiplayerPeer]. See [member MultiplayerPeer.transfer_channel].
The order of [param mode], [param sync] and [param transfer_mode] does not matter, but values related to the same argument must not be used more than once. [param transfer_channel] always has to be the 4th argument (you must specify 3 preceding arguments).
diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp
index 0d8453738d..26f326838c 100644
--- a/modules/gdscript/editor/gdscript_docgen.cpp
+++ b/modules/gdscript/editor/gdscript_docgen.cpp
@@ -101,14 +101,16 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
doc.inherits = p_script->native->get_name();
}
- doc.brief_description = p_class->doc_brief_description;
- doc.description = p_class->doc_description;
- for (const Pair<String, String> &p : p_class->doc_tutorials) {
+ doc.brief_description = p_class->doc_data.brief;
+ doc.description = p_class->doc_data.description;
+ for (const Pair<String, String> &p : p_class->doc_data.tutorials) {
DocData::TutorialDoc td;
td.title = p.first;
td.link = p.second;
doc.tutorials.append(td);
}
+ doc.is_deprecated = p_class->doc_data.is_deprecated;
+ doc.is_experimental = p_class->doc_data.is_experimental;
for (const GDP::ClassNode::Member &member : p_class->members) {
switch (member.type) {
@@ -130,7 +132,9 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
p_script->member_lines[const_name] = m_const->start_line;
DocData::ConstantDoc const_doc;
- DocData::constant_doc_from_variant(const_doc, const_name, m_const->initializer->reduced_value, m_const->doc_description);
+ DocData::constant_doc_from_variant(const_doc, const_name, m_const->initializer->reduced_value, m_const->doc_data.description);
+ const_doc.is_deprecated = m_const->doc_data.is_deprecated;
+ const_doc.is_experimental = m_const->doc_data.is_experimental;
doc.constants.push_back(const_doc);
} break;
@@ -153,7 +157,9 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
}
DocData::MethodDoc method_doc;
- DocData::method_doc_from_methodinfo(method_doc, mi, m_func->doc_description);
+ DocData::method_doc_from_methodinfo(method_doc, mi, m_func->doc_data.description);
+ method_doc.is_deprecated = m_func->doc_data.is_deprecated;
+ method_doc.is_experimental = m_func->doc_data.is_experimental;
doc.methods.push_back(method_doc);
} break;
@@ -172,7 +178,9 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
}
DocData::MethodDoc signal_doc;
- DocData::signal_doc_from_methodinfo(signal_doc, mi, m_signal->doc_description);
+ DocData::signal_doc_from_methodinfo(signal_doc, mi, m_signal->doc_data.description);
+ signal_doc.is_deprecated = m_signal->doc_data.is_deprecated;
+ signal_doc.is_experimental = m_signal->doc_data.is_experimental;
doc.signals.push_back(signal_doc);
} break;
@@ -185,7 +193,9 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
DocData::PropertyDoc prop_doc;
prop_doc.name = var_name;
- prop_doc.description = m_var->doc_description;
+ prop_doc.description = m_var->doc_data.description;
+ prop_doc.is_deprecated = m_var->doc_data.is_deprecated;
+ prop_doc.is_experimental = m_var->doc_data.is_experimental;
GDType dt = m_var->get_datatype();
switch (dt.kind) {
@@ -236,15 +246,21 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
p_script->member_lines[name] = m_enum->start_line;
- doc.enums[name] = m_enum->doc_description;
+ DocData::EnumDoc enum_doc;
+ enum_doc.description = m_enum->doc_data.description;
+ enum_doc.is_deprecated = m_enum->doc_data.is_deprecated;
+ enum_doc.is_experimental = m_enum->doc_data.is_experimental;
+ doc.enums[name] = enum_doc;
for (const GDP::EnumNode::Value &val : m_enum->values) {
DocData::ConstantDoc const_doc;
const_doc.name = val.identifier->name;
const_doc.value = String(Variant(val.value));
const_doc.is_value_valid = true;
- const_doc.description = val.doc_description;
const_doc.enumeration = name;
+ const_doc.description = val.doc_data.description;
+ const_doc.is_deprecated = val.doc_data.is_deprecated;
+ const_doc.is_experimental = val.doc_data.is_experimental;
doc.constants.push_back(const_doc);
}
@@ -257,10 +273,12 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
p_script->member_lines[name] = m_enum_val.identifier->start_line;
- DocData::ConstantDoc constant_doc;
- constant_doc.enumeration = "@unnamed_enums";
- DocData::constant_doc_from_variant(constant_doc, name, m_enum_val.value, m_enum_val.doc_description);
- doc.constants.push_back(constant_doc);
+ DocData::ConstantDoc const_doc;
+ DocData::constant_doc_from_variant(const_doc, name, m_enum_val.value, m_enum_val.doc_data.description);
+ const_doc.enumeration = "@unnamed_enums";
+ const_doc.is_deprecated = m_enum_val.doc_data.is_deprecated;
+ const_doc.is_experimental = m_enum_val.doc_data.is_experimental;
+ doc.constants.push_back(const_doc);
} break;
case GDP::ClassNode::Member::GROUP:
case GDP::ClassNode::Member::UNDEFINED:
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
index e17e748d7b..064143f400 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
@@ -31,6 +31,7 @@
#include "gdscript_translation_parser_plugin.h"
#include "../gdscript.h"
+#include "../gdscript_analyzer.h"
#include "core/io/resource_loader.h"
@@ -58,10 +59,11 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
GDScriptParser parser;
err = parser.parse(source_code, p_path, false);
- if (err != OK) {
- ERR_PRINT("Failed to parse with GDScript with GDScriptParser.");
- return err;
- }
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to parse GDScript with GDScriptParser.");
+
+ GDScriptAnalyzer analyzer(&parser);
+ err = analyzer.analyze();
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to analyze GDScript with GDScriptAnalyzer.");
// Traverse through the parsed tree from GDScriptParser.
GDScriptParser::ClassNode *c = parser.get_tree();
@@ -70,6 +72,11 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
return OK;
}
+bool GDScriptEditorTranslationParserPlugin::_is_constant_string(const GDScriptParser::ExpressionNode *p_expression) {
+ ERR_FAIL_NULL_V(p_expression, false);
+ return p_expression->is_constant && (p_expression->reduced_value.get_type() == Variant::STRING || p_expression->reduced_value.get_type() == Variant::STRING_NAME);
+}
+
void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser::ClassNode *p_class) {
for (int i = 0; i < p_class->members.size(); i++) {
const GDScriptParser::ClassNode::Member &m = p_class->members[i];
@@ -105,15 +112,15 @@ void GDScriptEditorTranslationParserPlugin::_traverse_block(const GDScriptParser
const Vector<GDScriptParser::Node *> &statements = p_suite->statements;
for (int i = 0; i < statements.size(); i++) {
- GDScriptParser::Node *statement = statements[i];
+ const GDScriptParser::Node *statement = statements[i];
// Statements with Node type constant, break, continue, pass, breakpoint are skipped because they can't contain translatable strings.
switch (statement->type) {
case GDScriptParser::Node::VARIABLE:
- _assess_expression(static_cast<GDScriptParser::VariableNode *>(statement)->initializer);
+ _assess_expression(static_cast<const GDScriptParser::VariableNode *>(statement)->initializer);
break;
case GDScriptParser::Node::IF: {
- GDScriptParser::IfNode *if_node = static_cast<GDScriptParser::IfNode *>(statement);
+ const GDScriptParser::IfNode *if_node = static_cast<const GDScriptParser::IfNode *>(statement);
_assess_expression(if_node->condition);
//FIXME : if the elif logic is changed in GDScriptParser, then this probably will have to change as well. See GDScriptParser::TreePrinter::print_if().
_traverse_block(if_node->true_block);
@@ -121,19 +128,19 @@ void GDScriptEditorTranslationParserPlugin::_traverse_block(const GDScriptParser
break;
}
case GDScriptParser::Node::FOR: {
- GDScriptParser::ForNode *for_node = static_cast<GDScriptParser::ForNode *>(statement);
+ const GDScriptParser::ForNode *for_node = static_cast<const GDScriptParser::ForNode *>(statement);
_assess_expression(for_node->list);
_traverse_block(for_node->loop);
break;
}
case GDScriptParser::Node::WHILE: {
- GDScriptParser::WhileNode *while_node = static_cast<GDScriptParser::WhileNode *>(statement);
+ const GDScriptParser::WhileNode *while_node = static_cast<const GDScriptParser::WhileNode *>(statement);
_assess_expression(while_node->condition);
_traverse_block(while_node->loop);
break;
}
case GDScriptParser::Node::MATCH: {
- GDScriptParser::MatchNode *match_node = static_cast<GDScriptParser::MatchNode *>(statement);
+ const GDScriptParser::MatchNode *match_node = static_cast<const GDScriptParser::MatchNode *>(statement);
_assess_expression(match_node->test);
for (int j = 0; j < match_node->branches.size(); j++) {
_traverse_block(match_node->branches[j]->block);
@@ -141,24 +148,24 @@ void GDScriptEditorTranslationParserPlugin::_traverse_block(const GDScriptParser
break;
}
case GDScriptParser::Node::RETURN:
- _assess_expression(static_cast<GDScriptParser::ReturnNode *>(statement)->return_value);
+ _assess_expression(static_cast<const GDScriptParser::ReturnNode *>(statement)->return_value);
break;
case GDScriptParser::Node::ASSERT:
- _assess_expression((static_cast<GDScriptParser::AssertNode *>(statement))->condition);
+ _assess_expression((static_cast<const GDScriptParser::AssertNode *>(statement))->condition);
break;
case GDScriptParser::Node::ASSIGNMENT:
- _assess_assignment(static_cast<GDScriptParser::AssignmentNode *>(statement));
+ _assess_assignment(static_cast<const GDScriptParser::AssignmentNode *>(statement));
break;
default:
if (statement->is_expression()) {
- _assess_expression(static_cast<GDScriptParser::ExpressionNode *>(statement));
+ _assess_expression(static_cast<const GDScriptParser::ExpressionNode *>(statement));
}
break;
}
}
}
-void GDScriptEditorTranslationParserPlugin::_assess_expression(GDScriptParser::ExpressionNode *p_expression) {
+void GDScriptEditorTranslationParserPlugin::_assess_expression(const GDScriptParser::ExpressionNode *p_expression) {
// Explore all ExpressionNodes to find CallNodes which contain translation strings, such as tr(), set_text() etc.
// tr() can be embedded quite deep within multiple ExpressionNodes so need to dig down to search through all ExpressionNodes.
if (!p_expression) {
@@ -169,30 +176,30 @@ void GDScriptEditorTranslationParserPlugin::_assess_expression(GDScriptParser::E
// containing translation strings.
switch (p_expression->type) {
case GDScriptParser::Node::ARRAY: {
- GDScriptParser::ArrayNode *array_node = static_cast<GDScriptParser::ArrayNode *>(p_expression);
+ const GDScriptParser::ArrayNode *array_node = static_cast<const GDScriptParser::ArrayNode *>(p_expression);
for (int i = 0; i < array_node->elements.size(); i++) {
_assess_expression(array_node->elements[i]);
}
break;
}
case GDScriptParser::Node::ASSIGNMENT:
- _assess_assignment(static_cast<GDScriptParser::AssignmentNode *>(p_expression));
+ _assess_assignment(static_cast<const GDScriptParser::AssignmentNode *>(p_expression));
break;
case GDScriptParser::Node::BINARY_OPERATOR: {
- GDScriptParser::BinaryOpNode *binary_op_node = static_cast<GDScriptParser::BinaryOpNode *>(p_expression);
+ const GDScriptParser::BinaryOpNode *binary_op_node = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);
_assess_expression(binary_op_node->left_operand);
_assess_expression(binary_op_node->right_operand);
break;
}
case GDScriptParser::Node::CALL: {
- GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_expression);
+ const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(p_expression);
_extract_from_call(call_node);
for (int i = 0; i < call_node->arguments.size(); i++) {
_assess_expression(call_node->arguments[i]);
}
} break;
case GDScriptParser::Node::DICTIONARY: {
- GDScriptParser::DictionaryNode *dict_node = static_cast<GDScriptParser::DictionaryNode *>(p_expression);
+ const GDScriptParser::DictionaryNode *dict_node = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);
for (int i = 0; i < dict_node->elements.size(); i++) {
_assess_expression(dict_node->elements[i].key);
_assess_expression(dict_node->elements[i].value);
@@ -200,7 +207,7 @@ void GDScriptEditorTranslationParserPlugin::_assess_expression(GDScriptParser::E
break;
}
case GDScriptParser::Node::TERNARY_OPERATOR: {
- GDScriptParser::TernaryOpNode *ternary_op_node = static_cast<GDScriptParser::TernaryOpNode *>(p_expression);
+ const GDScriptParser::TernaryOpNode *ternary_op_node = static_cast<const GDScriptParser::TernaryOpNode *>(p_expression);
_assess_expression(ternary_op_node->condition);
_assess_expression(ternary_op_node->true_expr);
_assess_expression(ternary_op_node->false_expr);
@@ -211,39 +218,39 @@ void GDScriptEditorTranslationParserPlugin::_assess_expression(GDScriptParser::E
}
}
-void GDScriptEditorTranslationParserPlugin::_assess_assignment(GDScriptParser::AssignmentNode *p_assignment) {
+void GDScriptEditorTranslationParserPlugin::_assess_assignment(const GDScriptParser::AssignmentNode *p_assignment) {
// Extract the translatable strings coming from assignments. For example, get_node("Label").text = "____"
StringName assignee_name;
if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
- assignee_name = static_cast<GDScriptParser::IdentifierNode *>(p_assignment->assignee)->name;
+ assignee_name = static_cast<const GDScriptParser::IdentifierNode *>(p_assignment->assignee)->name;
} else if (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT) {
- assignee_name = static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->attribute->name;
+ assignee_name = static_cast<const GDScriptParser::SubscriptNode *>(p_assignment->assignee)->attribute->name;
}
- if (assignment_patterns.has(assignee_name) && p_assignment->assigned_value->type == GDScriptParser::Node::LITERAL) {
- // If the assignment is towards one of the extract patterns (text, tooltip_text etc.), and the value is a string literal, we collect the string.
- ids->push_back(static_cast<GDScriptParser::LiteralNode *>(p_assignment->assigned_value)->value);
+ if (assignment_patterns.has(assignee_name) && _is_constant_string(p_assignment->assigned_value)) {
+ // If the assignment is towards one of the extract patterns (text, tooltip_text etc.), and the value is a constant string, we collect the string.
+ ids->push_back(p_assignment->assigned_value->reduced_value);
} else if (assignee_name == fd_filters && p_assignment->assigned_value->type == GDScriptParser::Node::CALL) {
// FileDialog.filters accepts assignment in the form of PackedStringArray. For example,
// get_node("FileDialog").filters = PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]).
- GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value);
+ const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(p_assignment->assigned_value);
if (call_node->arguments[0]->type == GDScriptParser::Node::ARRAY) {
- GDScriptParser::ArrayNode *array_node = static_cast<GDScriptParser::ArrayNode *>(call_node->arguments[0]);
+ const GDScriptParser::ArrayNode *array_node = static_cast<const GDScriptParser::ArrayNode *>(call_node->arguments[0]);
// Extract the name in "extension ; name" of PackedStringArray.
for (int i = 0; i < array_node->elements.size(); i++) {
- _extract_fd_literals(array_node->elements[i]);
+ _extract_fd_constant_strings(array_node->elements[i]);
}
}
} else {
- // If the assignee is not in extract patterns or the assigned_value is not Literal type, try to see if the assigned_value contains tr().
+ // If the assignee is not in extract patterns or the assigned_value is not a constant string, try to see if the assigned_value contains tr().
_assess_expression(p_assignment->assigned_value);
}
}
-void GDScriptEditorTranslationParserPlugin::_extract_from_call(GDScriptParser::CallNode *p_call) {
+void GDScriptEditorTranslationParserPlugin::_extract_from_call(const GDScriptParser::CallNode *p_call) {
// Extract the translatable strings coming from function calls. For example:
// tr("___"), get_node("Label").set_text("____"), get_node("LineEdit").set_placeholder("____").
@@ -257,8 +264,8 @@ void GDScriptEditorTranslationParserPlugin::_extract_from_call(GDScriptParser::C
if (function_name == tr_func) {
// Extract from tr(id, ctx).
for (int i = 0; i < p_call->arguments.size(); i++) {
- if (p_call->arguments[i]->type == GDScriptParser::Node::LITERAL) {
- id_ctx_plural.write[i] = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[i])->value;
+ if (_is_constant_string(p_call->arguments[i])) {
+ id_ctx_plural.write[i] = p_call->arguments[i]->reduced_value;
} else {
// Avoid adding something like tr("Flying dragon", var_context_level_1). We want to extract both id and context together.
extract_id_ctx_plural = false;
@@ -278,8 +285,8 @@ void GDScriptEditorTranslationParserPlugin::_extract_from_call(GDScriptParser::C
continue;
}
- if (p_call->arguments[indices[i]]->type == GDScriptParser::Node::LITERAL) {
- id_ctx_plural.write[i] = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[indices[i]])->value;
+ if (_is_constant_string(p_call->arguments[indices[i]])) {
+ id_ctx_plural.write[i] = p_call->arguments[indices[i]]->reduced_value;
} else {
extract_id_ctx_plural = false;
}
@@ -288,45 +295,43 @@ void GDScriptEditorTranslationParserPlugin::_extract_from_call(GDScriptParser::C
ids_ctx_plural->push_back(id_ctx_plural);
}
} else if (first_arg_patterns.has(function_name)) {
- // Extracting argument with only string literals. In other words, not extracting something like set_text("hello " + some_var).
- if (p_call->arguments[0]->type == GDScriptParser::Node::LITERAL) {
- ids->push_back(static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[0])->value);
+ if (_is_constant_string(p_call->arguments[0])) {
+ ids->push_back(p_call->arguments[0]->reduced_value);
}
} else if (second_arg_patterns.has(function_name)) {
- if (p_call->arguments[1]->type == GDScriptParser::Node::LITERAL) {
- ids->push_back(static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[1])->value);
+ if (_is_constant_string(p_call->arguments[1])) {
+ ids->push_back(p_call->arguments[1]->reduced_value);
}
} else if (function_name == fd_add_filter) {
// Extract the 'JPE Images' in this example - get_node("FileDialog").add_filter("*.jpg; JPE Images").
- _extract_fd_literals(p_call->arguments[0]);
-
+ _extract_fd_constant_strings(p_call->arguments[0]);
} else if (function_name == fd_set_filter && p_call->arguments[0]->type == GDScriptParser::Node::CALL) {
// FileDialog.set_filters() accepts assignment in the form of PackedStringArray. For example,
// get_node("FileDialog").set_filters( PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"])).
- GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_call->arguments[0]);
+ const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(p_call->arguments[0]);
if (call_node->arguments[0]->type == GDScriptParser::Node::ARRAY) {
- GDScriptParser::ArrayNode *array_node = static_cast<GDScriptParser::ArrayNode *>(call_node->arguments[0]);
+ const GDScriptParser::ArrayNode *array_node = static_cast<const GDScriptParser::ArrayNode *>(call_node->arguments[0]);
for (int i = 0; i < array_node->elements.size(); i++) {
- _extract_fd_literals(array_node->elements[i]);
+ _extract_fd_constant_strings(array_node->elements[i]);
}
}
}
if (p_call->callee && p_call->callee->type == GDScriptParser::Node::SUBSCRIPT) {
- GDScriptParser::SubscriptNode *subscript_node = static_cast<GDScriptParser::SubscriptNode *>(p_call->callee);
+ const GDScriptParser::SubscriptNode *subscript_node = static_cast<const GDScriptParser::SubscriptNode *>(p_call->callee);
if (subscript_node->base && subscript_node->base->type == GDScriptParser::Node::CALL) {
- GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(subscript_node->base);
+ const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(subscript_node->base);
_extract_from_call(call_node);
}
}
}
-void GDScriptEditorTranslationParserPlugin::_extract_fd_literals(GDScriptParser::ExpressionNode *p_expression) {
+void GDScriptEditorTranslationParserPlugin::_extract_fd_constant_strings(const GDScriptParser::ExpressionNode *p_expression) {
// Extract the name in "extension ; name".
- if (p_expression->type == GDScriptParser::Node::LITERAL) {
- String arg_val = String(static_cast<GDScriptParser::LiteralNode *>(p_expression)->value);
+ if (_is_constant_string(p_expression)) {
+ String arg_val = p_expression->reduced_value;
PackedStringArray arr = arg_val.split(";", true);
if (arr.size() != 2) {
ERR_PRINT("Argument for setting FileDialog has bad format.");
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index 421030e49a..580c2a80cd 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -53,15 +53,17 @@ class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlug
StringName fd_set_filter = "set_filters";
StringName fd_filters = "filters";
+ static bool _is_constant_string(const GDScriptParser::ExpressionNode *p_expression);
+
void _traverse_class(const GDScriptParser::ClassNode *p_class);
void _traverse_function(const GDScriptParser::FunctionNode *p_func);
void _traverse_block(const GDScriptParser::SuiteNode *p_suite);
void _read_variable(const GDScriptParser::VariableNode *p_var);
- void _assess_expression(GDScriptParser::ExpressionNode *p_expression);
- void _assess_assignment(GDScriptParser::AssignmentNode *p_assignment);
- void _extract_from_call(GDScriptParser::CallNode *p_call);
- void _extract_fd_literals(GDScriptParser::ExpressionNode *p_expression);
+ void _assess_expression(const GDScriptParser::ExpressionNode *p_expression);
+ void _assess_assignment(const GDScriptParser::AssignmentNode *p_assignment);
+ void _extract_from_call(const GDScriptParser::CallNode *p_call);
+ void _extract_fd_constant_strings(const GDScriptParser::ExpressionNode *p_expression);
public:
virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) override;
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 3d6d133579..0bf9f72a2c 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 {
@@ -2094,10 +2094,7 @@ String GDScriptLanguage::get_extension() const {
}
void GDScriptLanguage::finish() {
- if (_call_stack) {
- memdelete_arr(_call_stack);
- _call_stack = nullptr;
- }
+ _call_stack.free();
// Clear the cache before parsing the script_list
GDScriptCache::clear();
@@ -2140,12 +2137,12 @@ void GDScriptLanguage::profiling_start() {
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
- elem->self()->profile.call_count = 0;
- elem->self()->profile.self_time = 0;
- elem->self()->profile.total_time = 0;
- elem->self()->profile.frame_call_count = 0;
- elem->self()->profile.frame_self_time = 0;
- elem->self()->profile.frame_total_time = 0;
+ elem->self()->profile.call_count.set(0);
+ elem->self()->profile.self_time.set(0);
+ elem->self()->profile.total_time.set(0);
+ elem->self()->profile.frame_call_count.set(0);
+ elem->self()->profile.frame_self_time.set(0);
+ elem->self()->profile.frame_total_time.set(0);
elem->self()->profile.last_frame_call_count = 0;
elem->self()->profile.last_frame_self_time = 0;
elem->self()->profile.last_frame_total_time = 0;
@@ -2175,9 +2172,9 @@ int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr,
if (current >= p_info_max) {
break;
}
- p_info_arr[current].call_count = elem->self()->profile.call_count;
- p_info_arr[current].self_time = elem->self()->profile.self_time;
- p_info_arr[current].total_time = elem->self()->profile.total_time;
+ p_info_arr[current].call_count = elem->self()->profile.call_count.get();
+ p_info_arr[current].self_time = elem->self()->profile.self_time.get();
+ p_info_arr[current].total_time = elem->self()->profile.total_time.get();
p_info_arr[current].signature = elem->self()->profile.signature;
elem = elem->next();
current++;
@@ -2395,12 +2392,12 @@ void GDScriptLanguage::frame() {
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
- elem->self()->profile.last_frame_call_count = elem->self()->profile.frame_call_count;
- elem->self()->profile.last_frame_self_time = elem->self()->profile.frame_self_time;
- elem->self()->profile.last_frame_total_time = elem->self()->profile.frame_total_time;
- elem->self()->profile.frame_call_count = 0;
- elem->self()->profile.frame_self_time = 0;
- elem->self()->profile.frame_total_time = 0;
+ elem->self()->profile.last_frame_call_count = elem->self()->profile.frame_call_count.get();
+ elem->self()->profile.last_frame_self_time = elem->self()->profile.frame_self_time.get();
+ elem->self()->profile.last_frame_total_time = elem->self()->profile.frame_total_time.get();
+ elem->self()->profile.frame_call_count.set(0);
+ elem->self()->profile.frame_self_time.set(0);
+ elem->self()->profile.frame_total_time.set(0);
elem = elem->next();
}
}
@@ -2607,6 +2604,8 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
return c->identifier != nullptr ? String(c->identifier->name) : String();
}
+thread_local GDScriptLanguage::CallStack GDScriptLanguage::_call_stack;
+
GDScriptLanguage::GDScriptLanguage() {
calls = 0;
ERR_FAIL_COND(singleton);
@@ -2626,18 +2625,14 @@ GDScriptLanguage::GDScriptLanguage() {
profiling = false;
script_frame_time = 0;
- _debug_call_stack_pos = 0;
int dmcs = GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "512," + itos(GDScriptFunction::MAX_CALL_DEPTH - 1) + ",1"), 1024);
if (EngineDebugger::is_active()) {
//debugging enabled!
_debug_max_call_stack = dmcs;
- _call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1);
-
} else {
_debug_max_call_stack = 0;
- _call_stack = nullptr;
}
#ifdef DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index d131ec6ab1..c41b1a0def 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -364,12 +364,26 @@ class GDScriptLanguage : public ScriptLanguage {
int *line = nullptr;
};
- int _debug_parse_err_line;
- String _debug_parse_err_file;
- String _debug_error;
- int _debug_call_stack_pos;
- int _debug_max_call_stack;
- CallLevel *_call_stack = nullptr;
+ static thread_local int _debug_parse_err_line;
+ static thread_local String _debug_parse_err_file;
+ static thread_local String _debug_error;
+ struct CallStack {
+ CallLevel *levels = nullptr;
+ int stack_pos = 0;
+
+ void free() {
+ if (levels) {
+ memdelete(levels);
+ levels = nullptr;
+ }
+ }
+ ~CallStack() {
+ free();
+ }
+ };
+
+ static thread_local CallStack _call_stack;
+ int _debug_max_call_stack = 0;
void _add_global(const StringName &p_name, const Variant &p_value);
@@ -395,59 +409,51 @@ public:
bool debug_break_parse(const String &p_file, int p_line, const String &p_error);
_FORCE_INLINE_ void enter_function(GDScriptInstance *p_instance, GDScriptFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) {
- if (Thread::get_main_id() != Thread::get_caller_id()) {
- return; //no support for other threads than main for now
+ if (unlikely(_call_stack.levels == nullptr)) {
+ _call_stack.levels = memnew_arr(CallLevel, _debug_max_call_stack + 1);
}
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0) {
EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() + 1);
}
- if (_debug_call_stack_pos >= _debug_max_call_stack) {
+ if (_call_stack.stack_pos >= _debug_max_call_stack) {
//stack overflow
_debug_error = vformat("Stack overflow (stack size: %s). Check for infinite recursion in your script.", _debug_max_call_stack);
EngineDebugger::get_script_debugger()->debug(this);
return;
}
- _call_stack[_debug_call_stack_pos].stack = p_stack;
- _call_stack[_debug_call_stack_pos].instance = p_instance;
- _call_stack[_debug_call_stack_pos].function = p_function;
- _call_stack[_debug_call_stack_pos].ip = p_ip;
- _call_stack[_debug_call_stack_pos].line = p_line;
- _debug_call_stack_pos++;
+ _call_stack.levels[_call_stack.stack_pos].stack = p_stack;
+ _call_stack.levels[_call_stack.stack_pos].instance = p_instance;
+ _call_stack.levels[_call_stack.stack_pos].function = p_function;
+ _call_stack.levels[_call_stack.stack_pos].ip = p_ip;
+ _call_stack.levels[_call_stack.stack_pos].line = p_line;
+ _call_stack.stack_pos++;
}
_FORCE_INLINE_ void exit_function() {
- if (Thread::get_main_id() != Thread::get_caller_id()) {
- return; //no support for other threads than main for now
- }
-
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0) {
EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() - 1);
}
- if (_debug_call_stack_pos == 0) {
+ if (_call_stack.stack_pos == 0) {
_debug_error = "Stack Underflow (Engine Bug)";
EngineDebugger::get_script_debugger()->debug(this);
return;
}
- _debug_call_stack_pos--;
+ _call_stack.stack_pos--;
}
virtual Vector<StackInfo> debug_get_current_stack_info() override {
- if (Thread::get_main_id() != Thread::get_caller_id()) {
- return Vector<StackInfo>();
- }
-
Vector<StackInfo> csi;
- csi.resize(_debug_call_stack_pos);
- for (int i = 0; i < _debug_call_stack_pos; i++) {
- csi.write[_debug_call_stack_pos - i - 1].line = _call_stack[i].line ? *_call_stack[i].line : 0;
- if (_call_stack[i].function) {
- csi.write[_debug_call_stack_pos - i - 1].func = _call_stack[i].function->get_name();
- csi.write[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_script_path();
+ csi.resize(_call_stack.stack_pos);
+ for (int i = 0; i < _call_stack.stack_pos; i++) {
+ csi.write[_call_stack.stack_pos - i - 1].line = _call_stack.levels[i].line ? *_call_stack.levels[i].line : 0;
+ if (_call_stack.levels[i].function) {
+ csi.write[_call_stack.stack_pos - i - 1].func = _call_stack.levels[i].function->get_name();
+ csi.write[_call_stack.stack_pos - i - 1].file = _call_stack.levels[i].function->get_script()->get_script_path();
}
}
return csi;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index d3445b8cc0..cb04913620 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1553,7 +1553,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, visible_name, p_function->parameters[i]->identifier->name);
}
- is_shadowing(p_function->parameters[i]->identifier, "function parameter");
+ is_shadowing(p_function->parameters[i]->identifier, "function parameter", true);
#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
if (p_function->parameters[i]->initializer) {
@@ -1772,6 +1772,15 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
bool is_constant = p_assignable->type == GDScriptParser::Node::CONSTANT;
+#ifdef DEBUG_ENABLED
+ if (p_assignable->identifier != nullptr && p_assignable->identifier->suite != nullptr && p_assignable->identifier->suite->parent_block != nullptr) {
+ if (p_assignable->identifier->suite->parent_block->has_local(p_assignable->identifier->name)) {
+ const GDScriptParser::SuiteNode::Local &local = p_assignable->identifier->suite->parent_block->get_local(p_assignable->identifier->name);
+ parser->push_warning(p_assignable->identifier, GDScriptWarning::CONFUSABLE_LOCAL_DECLARATION, local.get_name(), p_assignable->identifier->name);
+ }
+ }
+#endif
+
GDScriptParser::DataType specified_type;
bool has_specified_type = p_assignable->datatype_specifier != nullptr;
if (has_specified_type) {
@@ -1874,9 +1883,8 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
} else if (p_variable->assignments == 0) {
parser->push_warning(p_variable, GDScriptWarning::UNASSIGNED_VARIABLE, p_variable->identifier->name);
}
-
- is_shadowing(p_variable->identifier, kind);
}
+ is_shadowing(p_variable->identifier, kind, p_is_local);
#endif
}
@@ -1889,9 +1897,8 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
if (p_constant->usages == 0) {
parser->push_warning(p_constant, GDScriptWarning::UNUSED_LOCAL_CONSTANT, p_constant->identifier->name);
}
-
- is_shadowing(p_constant->identifier, kind);
}
+ is_shadowing(p_constant->identifier, kind, p_is_local);
#endif
}
@@ -2052,7 +2059,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
p_for->set_datatype(p_for->loop->get_datatype());
#ifdef DEBUG_ENABLED
if (p_for->variable) {
- is_shadowing(p_for->variable, R"("for" iterator variable)");
+ is_shadowing(p_for->variable, R"("for" iterator variable)", true);
}
#endif
}
@@ -2148,7 +2155,7 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
}
p_match_pattern->bind->set_datatype(result);
#ifdef DEBUG_ENABLED
- is_shadowing(p_match_pattern->bind, "pattern bind");
+ is_shadowing(p_match_pattern->bind, "pattern bind", true);
if (p_match_pattern->bind->usages == 0 && !String(p_match_pattern->bind->name).begins_with("_")) {
parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNUSED_VARIABLE, p_match_pattern->bind->name);
}
@@ -3520,12 +3527,14 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
case GDScriptParser::ClassNode::Member::FUNCTION: {
if (is_base && (!base.is_meta_type || member.function->is_static)) {
p_identifier->set_datatype(make_callable_type(member.function->info));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
return;
}
} break;
case GDScriptParser::ClassNode::Member::CLASS: {
reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
return;
}
@@ -3664,9 +3673,17 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
found_source = true;
} break;
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
+ case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+ case GDScriptParser::IdentifierNode::MEMBER_CLASS:
break;
}
+#ifdef DEBUG_ENABLED
+ if (!found_source && p_identifier->suite != nullptr && p_identifier->suite->has_local(p_identifier->name)) {
+ parser->push_warning(p_identifier, GDScriptWarning::CONFUSABLE_LOCAL_USAGE, p_identifier->name);
+ }
+#endif
+
// Not a local, so check members.
if (!found_source) {
reduce_identifier_from_base(p_identifier);
@@ -4517,6 +4534,10 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo
return result;
}
+const HashMap<String, Ref<GDScriptParserRef>> &GDScriptAnalyzer::get_depended_parsers() {
+ return depended_parsers;
+}
+
GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType result;
result.is_constant = true;
@@ -4890,8 +4911,8 @@ void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
}
#ifdef DEBUG_ENABLED
-void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context) {
- const StringName &name = p_local->name;
+void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier, const String &p_context, const bool p_in_local_scope) {
+ const StringName &name = p_identifier->name;
GDScriptParser::DataType base = parser->current_class->get_datatype();
GDScriptParser::ClassNode *base_class = base.class_type;
@@ -4901,29 +4922,30 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
for (MethodInfo &info : gdscript_funcs) {
if (info.name == name) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in function");
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in function");
return;
}
}
-
if (Variant::has_utility_function(name)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in function");
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in function");
return;
} else if (ClassDB::class_exists(name)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "global class");
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "global class");
return;
} else if (GDScriptParser::get_builtin_type(name) != Variant::VARIANT_MAX) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in type");
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in type");
return;
}
}
- while (base_class != nullptr) {
- if (base_class->has_member(name)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE, p_context, p_local->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()));
- return;
+ if (p_in_local_scope) {
+ while (base_class != nullptr) {
+ if (base_class->has_member(name)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE, p_context, p_identifier->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()));
+ return;
+ }
+ base_class = base_class->base_type.class_type;
}
- base_class = base_class->base_type.class_type;
}
StringName parent = base.native_type;
@@ -4931,19 +4953,19 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
ERR_FAIL_COND_MSG(!class_exists(parent), "Non-existent native base class.");
if (ClassDB::has_method(parent, name, true)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "method", parent);
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "method", parent);
return;
} else if (ClassDB::has_signal(parent, name, true)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "signal", parent);
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "signal", parent);
return;
} else if (ClassDB::has_property(parent, name, true)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "property", parent);
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "property", parent);
return;
} else if (ClassDB::has_integer_constant(parent, name, true)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "constant", parent);
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "constant", parent);
return;
} else if (ClassDB::has_enum(parent, name, true)) {
- parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "enum", parent);
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "enum", parent);
return;
}
parent = ClassDB::get_parent_class(parent);
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 195e23b503..5bc2c89a87 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -133,7 +133,7 @@ class GDScriptAnalyzer {
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype);
#ifdef DEBUG_ENABLED
- void is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
+ void is_shadowing(GDScriptParser::IdentifierNode *p_identifier, const String &p_context, const bool p_in_local_scope);
#endif
public:
@@ -144,6 +144,7 @@ public:
Error analyze();
Variant make_variable_default_value(GDScriptParser::VariableNode *p_variable);
+ const HashMap<String, Ref<GDScriptParserRef>> &get_depended_parsers();
GDScriptAnalyzer(GDScriptParser *p_parser);
};
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 47cd3f768b..6057a00f9b 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -226,7 +226,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
if (opcodes.size()) {
function->code = opcodes;
- function->_code_ptr = &function->code[0];
+ function->_code_ptr = &function->code.write[0];
function->_code_size = opcodes.size();
} else {
@@ -577,6 +577,12 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va
append(Address());
append(p_target);
append(p_operator);
+ append(0); // Signature storage.
+ append(0); // Return type storage.
+ constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*(opcodes.ptr()));
+ for (int i = 0; i < _pointer_size; i++) {
+ append(0); // Space for function pointer.
+ }
}
void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
@@ -610,6 +616,12 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V
append(p_right_operand);
append(p_target);
append(p_operator);
+ append(0); // Signature storage.
+ append(0); // Return type storage.
+ constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*(opcodes.ptr()));
+ for (int i = 0; i < _pointer_size; i++) {
+ append(0); // Space for function pointer.
+ }
}
void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
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..2a52db4158 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -225,194 +225,211 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
StringName identifier = in->name;
- // Try function parameters.
- if (codegen.parameters.has(identifier)) {
- return codegen.parameters[identifier];
- }
-
- // Try local variables and constants.
- if (!p_initializer && codegen.locals.has(identifier)) {
- return codegen.locals[identifier];
- }
+ switch (in->source) {
+ // LOCALS.
+ case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
+ case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
+ case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
+ case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
+ case GDScriptParser::IdentifierNode::LOCAL_BIND: {
+ // Try function parameters.
+ if (codegen.parameters.has(identifier)) {
+ return codegen.parameters[identifier];
+ }
- // Try class members.
- if (_is_class_member_property(codegen, identifier)) {
- // Get property.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));
- gen->write_get_member(temp, identifier);
- return temp;
- }
+ // Try local variables and constants.
+ if (!p_initializer && codegen.locals.has(identifier)) {
+ return codegen.locals[identifier];
+ }
+ } break;
- // Try members.
- if (!codegen.function_node || !codegen.function_node->is_static) {
- // Try member variables.
- if (codegen.script->member_indices.has(identifier)) {
- if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {
- // Perform getter.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type);
- Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
- gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
+ // MEMBERS.
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+ case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: {
+ // Try class members.
+ if (_is_class_member_property(codegen, identifier)) {
+ // Get property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));
+ gen->write_get_member(temp, identifier);
return temp;
- } else {
- // No getter or inside getter: direct member access.
- int idx = codegen.script->member_indices[identifier].index;
- return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));
}
- }
- }
- // Try static variables.
- {
- GDScript *scr = codegen.script;
- while (scr) {
- if (scr->static_variables_indices.has(identifier)) {
- if (scr->static_variables_indices[identifier].getter != StringName() && scr->static_variables_indices[identifier].getter != codegen.function_name) {
- // Perform getter.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
- GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS);
- Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
- gen->write_call(temp, class_addr, scr->static_variables_indices[identifier].getter, args);
- return temp;
- } else {
- // No getter or inside getter: direct variable access.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
- GDScriptCodeGenerator::Address _class = codegen.add_constant(scr);
- int index = scr->static_variables_indices[identifier].index;
- gen->write_get_static_variable(temp, _class, index);
- return temp;
+ // Try members.
+ if (!codegen.function_node || !codegen.function_node->is_static) {
+ // Try member variables.
+ if (codegen.script->member_indices.has(identifier)) {
+ if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {
+ // Perform getter.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type);
+ Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
+ gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
+ return temp;
+ } else {
+ // No getter or inside getter: direct member access.
+ int idx = codegen.script->member_indices[identifier].index;
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));
+ }
}
}
- scr = scr->_base;
- }
- }
+ } break;
+ case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+ case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: {
+ // Try methods and signals (can be Callable and Signal).
+
+ // Search upwards through parent classes:
+ const GDScriptParser::ClassNode *base_class = codegen.class_node;
+ while (base_class != nullptr) {
+ if (base_class->has_member(identifier)) {
+ const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ // Get like it was a property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
+
+ gen->write_get_named(temp, identifier, self);
+ return temp;
+ }
+ }
+ base_class = base_class->base_type.class_type;
+ }
- // Try class constants.
- {
- GDScript *owner = codegen.script;
- while (owner) {
- GDScript *scr = owner;
+ // Try in native base.
+ GDScript *scr = codegen.script;
GDScriptNativeClass *nc = nullptr;
while (scr) {
- if (scr->constants.has(identifier)) {
- return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.
- }
if (scr->native.is_valid()) {
nc = scr->native.ptr();
}
scr = scr->_base;
}
- // Class C++ integer constant.
- if (nc) {
- bool success = false;
- int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
- if (success) {
- return codegen.add_constant(constant);
- }
- }
+ if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
+ // Get like it was a property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
- owner = owner->_owner;
- }
- }
+ gen->write_get_named(temp, identifier, self);
+ return temp;
+ }
+ } break;
+ case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
+ case GDScriptParser::IdentifierNode::MEMBER_CLASS: {
+ // Try class constants.
+ GDScript *owner = codegen.script;
+ while (owner) {
+ GDScript *scr = owner;
+ GDScriptNativeClass *nc = nullptr;
+ while (scr) {
+ if (scr->constants.has(identifier)) {
+ return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.
+ }
+ if (scr->native.is_valid()) {
+ nc = scr->native.ptr();
+ }
+ scr = scr->_base;
+ }
- // Try signals and methods (can be made callables).
- {
- // Search upwards through parent classes:
- const GDScriptParser::ClassNode *base_class = codegen.class_node;
- while (base_class != nullptr) {
- if (base_class->has_member(identifier)) {
- const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);
- if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
- // Get like it was a property.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
- GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
+ // Class C++ integer constant.
+ if (nc) {
+ bool success = false;
+ int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
+ if (success) {
+ return codegen.add_constant(constant);
+ }
+ }
- gen->write_get_named(temp, identifier, self);
- return temp;
+ owner = owner->_owner;
+ }
+ } break;
+ case GDScriptParser::IdentifierNode::STATIC_VARIABLE: {
+ // Try static variables.
+ GDScript *scr = codegen.script;
+ while (scr) {
+ if (scr->static_variables_indices.has(identifier)) {
+ if (scr->static_variables_indices[identifier].getter != StringName() && scr->static_variables_indices[identifier].getter != codegen.function_name) {
+ // Perform getter.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
+ GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS);
+ Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
+ gen->write_call(temp, class_addr, scr->static_variables_indices[identifier].getter, args);
+ return temp;
+ } else {
+ // No getter or inside getter: direct variable access.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
+ GDScriptCodeGenerator::Address _class = codegen.add_constant(scr);
+ int index = scr->static_variables_indices[identifier].index;
+ gen->write_get_static_variable(temp, _class, index);
+ return temp;
+ }
}
+ scr = scr->_base;
}
- base_class = base_class->base_type.class_type;
- }
+ } break;
- // Try in native base.
- GDScript *scr = codegen.script;
- GDScriptNativeClass *nc = nullptr;
- while (scr) {
- if (scr->native.is_valid()) {
- nc = scr->native.ptr();
+ // GLOBALS.
+ case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE: {
+ // Try globals.
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
+ // If it's an autoload singleton, we postpone to load it at runtime.
+ // This is so one autoload doesn't try to load another before it's compiled.
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
+ GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
+ gen->write_store_global(global, idx);
+ return global;
+ } else {
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
+ Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ return codegen.add_constant(global);
+ }
}
- scr = scr->_base;
- }
-
- if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
- // Get like it was a property.
- GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
- GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
-
- gen->write_get_named(temp, identifier, self);
- return temp;
- }
- }
-
- // Try globals.
- if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
- // If it's an autoload singleton, we postpone to load it at runtime.
- // This is so one autoload doesn't try to load another before it's compiled.
- HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
- GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
- gen->write_store_global(global, idx);
- return global;
- } else {
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
- Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];
- return codegen.add_constant(global);
- }
- }
- // Try global classes.
- if (ScriptServer::is_global_class(identifier)) {
- const GDScriptParser::ClassNode *class_node = codegen.class_node;
- while (class_node->outer) {
- class_node = class_node->outer;
- }
+ // Try global classes.
+ if (ScriptServer::is_global_class(identifier)) {
+ const GDScriptParser::ClassNode *class_node = codegen.class_node;
+ while (class_node->outer) {
+ class_node = class_node->outer;
+ }
- Ref<Resource> res;
+ Ref<Resource> res;
- if (class_node->identifier && class_node->identifier->name == identifier) {
- res = Ref<GDScript>(main_script);
- } else {
- String global_class_path = ScriptServer::get_global_class_path(identifier);
- if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
- Error err = OK;
- res = GDScriptCache::get_full_script(global_class_path, err);
- if (err != OK) {
- _set_error("Can't load global class " + String(identifier), p_expression);
- r_error = ERR_COMPILATION_FAILED;
- return GDScriptCodeGenerator::Address();
- }
- } else {
- res = ResourceLoader::load(global_class_path);
- if (res.is_null()) {
- _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
- r_error = ERR_COMPILATION_FAILED;
- return GDScriptCodeGenerator::Address();
+ if (class_node->identifier && class_node->identifier->name == identifier) {
+ res = Ref<GDScript>(main_script);
+ } else {
+ String global_class_path = ScriptServer::get_global_class_path(identifier);
+ if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
+ Error err = OK;
+ res = GDScriptCache::get_full_script(global_class_path, err);
+ if (err != OK) {
+ _set_error("Can't load global class " + String(identifier), p_expression);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
+ } else {
+ res = ResourceLoader::load(global_class_path);
+ if (res.is_null()) {
+ _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
+ }
}
- }
- }
- return codegen.add_constant(res);
- }
+ return codegen.add_constant(res);
+ }
#ifdef TOOLS_ENABLED
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
- GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type.
- gen->write_store_named_global(global, identifier);
- return global;
- }
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
+ GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type.
+ gen->write_store_named_global(global, identifier);
+ return global;
+ }
#endif
+ } break;
+ }
+
// Not found, error.
_set_error("Identifier not found: " + String(identifier), p_expression);
r_error = ERR_COMPILATION_FAILED;
@@ -2579,9 +2596,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 +2608,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;
@@ -2705,20 +2727,21 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
case GDScriptParser::ClassNode::Member::GROUP: {
const GDScriptParser::AnnotationNode *annotation = member.annotation;
- StringName name = annotation->export_info.name;
+ // Avoid name conflict. See GH-78252.
+ StringName name = vformat("@group_%d_%s", p_script->members.size(), annotation->export_info.name);
// This is not a normal member, but we need this to keep indices in order.
GDScript::MemberInfo minfo;
minfo.index = p_script->member_indices.size();
PropertyInfo prop_info;
- prop_info.name = name;
+ prop_info.name = annotation->export_info.name;
prop_info.usage = annotation->export_info.usage;
prop_info.hint_string = annotation->export_info.hint_string;
p_script->member_info[name] = prop_info;
p_script->member_indices[name] = minfo;
- p_script->members.insert(name);
+ p_script->members.insert(Variant());
} break;
default:
@@ -2968,7 +2991,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/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index ec1d0af329..438ec02740 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -113,6 +113,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
switch (opcode) {
case OPCODE_OPERATOR: {
+ constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr);
int operation = _code_ptr[ip + 4];
text += "operator ";
@@ -125,7 +126,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += " ";
text += DADDR(2);
- incr += 5;
+ incr += 7 + _pointer_size;
} break;
case OPCODE_OPERATOR_VALIDATED: {
text += "validated operator ";
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index cd34feb8b3..d27ea974e3 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -143,14 +143,26 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
#endif
if (err) {
if (r_errors) {
- for (const GDScriptParser::ParserError &E : parser.get_errors()) {
- const GDScriptParser::ParserError &pe = E;
+ for (const GDScriptParser::ParserError &pe : parser.get_errors()) {
ScriptLanguage::ScriptError e;
+ e.path = p_path;
e.line = pe.line;
e.column = pe.column;
e.message = pe.message;
r_errors->push_back(e);
}
+
+ for (KeyValue<String, Ref<GDScriptParserRef>> E : analyzer.get_depended_parsers()) {
+ GDScriptParser *depended_parser = E.value->get_parser();
+ for (const GDScriptParser::ParserError &pe : depended_parser->get_errors()) {
+ ScriptLanguage::ScriptError e;
+ e.path = E.key;
+ e.line = pe.line;
+ e.column = pe.column;
+ e.message = pe.message;
+ r_errors->push_back(e);
+ }
+ }
}
return false;
} else {
@@ -221,6 +233,10 @@ Script *GDScriptLanguage::create_script() const {
/* DEBUGGER FUNCTIONS */
+thread_local int GDScriptLanguage::_debug_parse_err_line = -1;
+thread_local String GDScriptLanguage::_debug_parse_err_file;
+thread_local String GDScriptLanguage::_debug_error;
+
bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
// break because of parse error
@@ -229,6 +245,9 @@ bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const
_debug_parse_err_file = p_file;
_debug_error = p_error;
EngineDebugger::get_script_debugger()->debug(this, false, true);
+ // Because this is thread local, clear the memory afterwards.
+ _debug_parse_err_file = String();
+ _debug_error = String();
return true;
} else {
return false;
@@ -236,12 +255,15 @@ bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const
}
bool GDScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) {
- if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
+ if (EngineDebugger::is_active()) {
_debug_parse_err_line = -1;
_debug_parse_err_file = "";
_debug_error = p_error;
bool is_error_breakpoint = p_error != "Breakpoint";
EngineDebugger::get_script_debugger()->debug(this, p_allow_continue, is_error_breakpoint);
+ // Because this is thread local, clear the memory afterwards.
+ _debug_parse_err_file = String();
+ _debug_error = String();
return true;
} else {
return false;
@@ -257,7 +279,7 @@ int GDScriptLanguage::debug_get_stack_level_count() const {
return 1;
}
- return _debug_call_stack_pos;
+ return _call_stack.stack_pos;
}
int GDScriptLanguage::debug_get_stack_level_line(int p_level) const {
@@ -265,11 +287,11 @@ int GDScriptLanguage::debug_get_stack_level_line(int p_level) const {
return _debug_parse_err_line;
}
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, -1);
+ ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, -1);
- int l = _debug_call_stack_pos - p_level - 1;
+ int l = _call_stack.stack_pos - p_level - 1;
- return *(_call_stack[l].line);
+ return *(_call_stack.levels[l].line);
}
String GDScriptLanguage::debug_get_stack_level_function(int p_level) const {
@@ -277,9 +299,9 @@ String GDScriptLanguage::debug_get_stack_level_function(int p_level) const {
return "";
}
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, "");
- int l = _debug_call_stack_pos - p_level - 1;
- return _call_stack[l].function->get_name();
+ ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, "");
+ int l = _call_stack.stack_pos - p_level - 1;
+ return _call_stack.levels[l].function->get_name();
}
String GDScriptLanguage::debug_get_stack_level_source(int p_level) const {
@@ -287,9 +309,9 @@ String GDScriptLanguage::debug_get_stack_level_source(int p_level) const {
return _debug_parse_err_file;
}
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, "");
- int l = _debug_call_stack_pos - p_level - 1;
- return _call_stack[l].function->get_source();
+ ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, "");
+ int l = _call_stack.stack_pos - p_level - 1;
+ return _call_stack.levels[l].function->get_source();
}
void GDScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
@@ -297,17 +319,17 @@ void GDScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p
return;
}
- ERR_FAIL_INDEX(p_level, _debug_call_stack_pos);
- int l = _debug_call_stack_pos - p_level - 1;
+ ERR_FAIL_INDEX(p_level, _call_stack.stack_pos);
+ int l = _call_stack.stack_pos - p_level - 1;
- GDScriptFunction *f = _call_stack[l].function;
+ GDScriptFunction *f = _call_stack.levels[l].function;
List<Pair<StringName, int>> locals;
- f->debug_get_stack_member_state(*_call_stack[l].line, &locals);
+ f->debug_get_stack_member_state(*_call_stack.levels[l].line, &locals);
for (const Pair<StringName, int> &E : locals) {
p_locals->push_back(E.first);
- p_values->push_back(_call_stack[l].stack[E.second]);
+ p_values->push_back(_call_stack.levels[l].stack[E.second]);
}
}
@@ -316,10 +338,10 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *
return;
}
- ERR_FAIL_INDEX(p_level, _debug_call_stack_pos);
- int l = _debug_call_stack_pos - p_level - 1;
+ ERR_FAIL_INDEX(p_level, _call_stack.stack_pos);
+ int l = _call_stack.stack_pos - p_level - 1;
- GDScriptInstance *instance = _call_stack[l].instance;
+ GDScriptInstance *instance = _call_stack.levels[l].instance;
if (!instance) {
return;
@@ -341,10 +363,10 @@ ScriptInstance *GDScriptLanguage::debug_get_stack_level_instance(int p_level) {
return nullptr;
}
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, nullptr);
+ ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, nullptr);
- int l = _debug_call_stack_pos - p_level - 1;
- ScriptInstance *instance = _call_stack[l].instance;
+ int l = _call_stack.stack_pos - p_level - 1;
+ ScriptInstance *instance = _call_stack.levels[l].instance;
return instance;
}
@@ -796,9 +818,10 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
node.insert_text = node.display.quote(p_quote_style);
r_result.insert(node.display, node);
- List<StringName> node_types;
- ClassDB::get_inheriters_from_class("Node", &node_types);
- for (const StringName &E : node_types) {
+
+ List<StringName> native_classes;
+ ClassDB::get_inheriters_from_class("Node", &native_classes);
+ for (const StringName &E : native_classes) {
if (!ClassDB::is_class_exposed(E)) {
continue;
}
@@ -806,6 +829,17 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
option.insert_text = option.display.quote(p_quote_style);
r_result.insert(option.display, option);
}
+
+ List<StringName> global_script_classes;
+ ScriptServer::get_global_class_list(&global_script_classes);
+ for (const StringName &E : global_script_classes) {
+ if (!ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(E), "Node")) {
+ continue;
+ }
+ ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
+ option.insert_text = option.display.quote(p_quote_style);
+ r_result.insert(option.display, option);
+ }
} else if (p_annotation->name == SNAME("@warning_ignore")) {
for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {
ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
@@ -1381,7 +1415,7 @@ struct RecursionCheck {
}
};
-static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
+static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type);
static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);
@@ -1435,17 +1469,17 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
} break;
case GDScriptParser::Node::SELF: {
if (p_context.current_class) {
- r_type.type.kind = GDScriptParser::DataType::CLASS;
- r_type.type.type_source = GDScriptParser::DataType::INFERRED;
- r_type.type.is_constant = true;
- r_type.type.class_type = p_context.current_class;
- r_type.value = p_context.base;
+ if (p_context.type != GDScriptParser::COMPLETION_SUPER_METHOD) {
+ r_type.type = p_context.current_class->get_datatype();
+ } else {
+ r_type.type = p_context.current_class->base_type;
+ }
found = true;
}
} break;
case GDScriptParser::Node::IDENTIFIER: {
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
- found = _guess_identifier_type(p_context, id->name, r_type);
+ found = _guess_identifier_type(p_context, id, r_type);
} break;
case GDScriptParser::Node::DICTIONARY: {
// Try to recreate the dictionary.
@@ -1888,7 +1922,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
return found;
}
-static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
+static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type) {
static int recursion_depth = 0;
RecursionCheck recursion(&recursion_depth);
if (unlikely(recursion.check())) {
@@ -1902,36 +1936,49 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
GDScriptParser::SuiteNode *suite = p_context.current_suite;
bool is_function_parameter = false;
- if (suite) {
- if (suite->has_local(p_identifier)) {
- const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier);
+ bool can_be_local = true;
+ switch (p_identifier->source) {
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+ case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
+ case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+ case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
+ case GDScriptParser::IdentifierNode::MEMBER_CLASS:
+ case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
+ case GDScriptParser::IdentifierNode::STATIC_VARIABLE:
+ can_be_local = false;
+ break;
+ default:
+ break;
+ }
- id_type = local.get_datatype();
+ if (can_be_local && suite && suite->has_local(p_identifier->name)) {
+ const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name);
- // Check initializer as the first assignment.
- switch (local.type) {
- case GDScriptParser::SuiteNode::Local::VARIABLE:
- if (local.variable->initializer) {
- last_assign_line = local.variable->initializer->end_line;
- last_assigned_expression = local.variable->initializer;
- }
- break;
- case GDScriptParser::SuiteNode::Local::CONSTANT:
- if (local.constant->initializer) {
- last_assign_line = local.constant->initializer->end_line;
- last_assigned_expression = local.constant->initializer;
- }
- break;
- case GDScriptParser::SuiteNode::Local::PARAMETER:
- if (local.parameter->initializer) {
- last_assign_line = local.parameter->initializer->end_line;
- last_assigned_expression = local.parameter->initializer;
- }
- is_function_parameter = true;
- break;
- default:
- break;
- }
+ id_type = local.get_datatype();
+
+ // Check initializer as the first assignment.
+ switch (local.type) {
+ case GDScriptParser::SuiteNode::Local::VARIABLE:
+ if (local.variable->initializer) {
+ last_assign_line = local.variable->initializer->end_line;
+ last_assigned_expression = local.variable->initializer;
+ }
+ break;
+ case GDScriptParser::SuiteNode::Local::CONSTANT:
+ if (local.constant->initializer) {
+ last_assign_line = local.constant->initializer->end_line;
+ last_assigned_expression = local.constant->initializer;
+ }
+ break;
+ case GDScriptParser::SuiteNode::Local::PARAMETER:
+ if (local.parameter->initializer) {
+ last_assign_line = local.parameter->initializer->end_line;
+ last_assigned_expression = local.parameter->initializer;
+ }
+ is_function_parameter = true;
+ break;
+ default:
+ break;
}
}
@@ -1946,7 +1993,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
const GDScriptParser::AssignmentNode *assign = static_cast<const GDScriptParser::AssignmentNode *>(suite->statements[i]);
if (assign->end_line > last_assign_line && assign->assignee && assign->assigned_value && assign->assignee->type == GDScriptParser::Node::IDENTIFIER) {
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->assignee);
- if (id->name == p_identifier) {
+ if (id->name == p_identifier->name && id->source == p_identifier->source) {
last_assign_line = assign->assigned_value->end_line;
last_assigned_expression = assign->assigned_value;
}
@@ -1964,7 +2011,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
// Credit: Zylann.
// TODO: this could be hacked to detect ANDed conditions too...
const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(suite->parent_if->condition);
- if (type_test->operand && type_test->test_type && type_test->operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->name == p_identifier) {
+ if (type_test->operand && type_test->test_type && type_test->operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->name == p_identifier->name && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->source == p_identifier->source) {
// Bingo.
GDScriptParser::CompletionContext c = p_context;
c.current_line = type_test->operand->start_line;
@@ -2000,8 +2047,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
case GDScriptParser::DataType::CLASS:
if (base_type.class_type->has_function(p_context.current_function->identifier->name)) {
GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;
- if (parent_function->parameters_indices.has(p_identifier)) {
- const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier]];
+ if (parent_function->parameters_indices.has(p_identifier->name)) {
+ const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier->name]];
if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
id_type = parameter->get_datatype();
}
@@ -2026,7 +2073,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
MethodInfo info;
if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) {
for (const PropertyInfo &E : info.arguments) {
- if (E.name == p_identifier) {
+ if (E.name == p_identifier->name) {
r_type = _type_from_property(E);
return true;
}
@@ -2054,14 +2101,14 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
base.type.class_type = p_context.current_class;
base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static;
- if (_guess_identifier_type_from_base(p_context, base, p_identifier, r_type)) {
+ if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, r_type)) {
return true;
}
}
// Check global scripts.
- if (ScriptServer::is_global_class(p_identifier)) {
- String script = ScriptServer::get_global_class_path(p_identifier);
+ if (ScriptServer::is_global_class(p_identifier->name)) {
+ String script = ScriptServer::get_global_class_path(p_identifier->name);
if (script.to_lower().ends_with(".gd")) {
Error err = OK;
Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err);
@@ -2077,7 +2124,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
return true;
}
} else {
- Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
+ Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier->name));
if (scr.is_valid()) {
r_type = _type_from_variant(scr);
r_type.type.is_meta_type = true;
@@ -2088,20 +2135,20 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
}
// Check global variables (including autoloads).
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
- r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier]);
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier->name)) {
+ r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name]);
return true;
}
// Check ClassDB.
- if (ClassDB::class_exists(p_identifier) && ClassDB::is_class_exposed(p_identifier)) {
+ if (ClassDB::class_exists(p_identifier->name) && ClassDB::is_class_exposed(p_identifier->name)) {
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.kind = GDScriptParser::DataType::NATIVE;
- r_type.type.native_type = p_identifier;
+ r_type.type.native_type = p_identifier->name;
r_type.type.is_constant = true;
- if (Engine::get_singleton()->has_singleton(p_identifier)) {
+ if (Engine::get_singleton()->has_singleton(p_identifier->name)) {
r_type.type.is_meta_type = false;
- r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
+ r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier->name);
} else {
r_type.type.is_meta_type = true;
r_type.value = Variant();
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 9bbfb14f31..5230773c13 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -473,7 +473,7 @@ private:
MethodBind **_methods_ptr = nullptr;
int _lambdas_count = 0;
GDScriptFunction **_lambdas_ptr = nullptr;
- const int *_code_ptr = nullptr;
+ int *_code_ptr = nullptr;
int _code_size = 0;
int _argument_count = 0;
int _stack_size = 0;
@@ -539,12 +539,12 @@ private:
struct Profile {
StringName signature;
- uint64_t call_count = 0;
- uint64_t self_time = 0;
- uint64_t total_time = 0;
- uint64_t frame_call_count = 0;
- uint64_t frame_self_time = 0;
- uint64_t frame_total_time = 0;
+ SafeNumeric<uint64_t> call_count;
+ SafeNumeric<uint64_t> self_time;
+ SafeNumeric<uint64_t> total_time;
+ SafeNumeric<uint64_t> frame_call_count;
+ SafeNumeric<uint64_t> frame_self_time;
+ SafeNumeric<uint64_t> frame_total_time;
uint64_t last_frame_call_count = 0;
uint64_t last_frame_self_time = 0;
uint64_t last_frame_total_time = 0;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index e17ee0668f..debc85ebbf 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -571,8 +571,8 @@ void GDScriptParser::parse_program() {
class_doc_line = MIN(class_doc_line, E.key);
}
}
- if (has_comment(class_doc_line)) {
- get_class_doc_comment(class_doc_line, head->doc_brief_description, head->doc_description, head->doc_tutorials, false);
+ if (has_comment(class_doc_line, true)) {
+ head->doc_data = parse_class_doc_comment(class_doc_line, false);
}
#endif // TOOLS_ENABLED
@@ -771,32 +771,22 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(b
// Check whether current line has a doc comment
if (has_comment(previous.start_line, true)) {
- member->doc_description = get_doc_comment(previous.start_line, true);
+ if constexpr (std::is_same_v<T, ClassNode>) {
+ member->doc_data = parse_class_doc_comment(previous.start_line, true, true);
+ } else {
+ member->doc_data = parse_doc_comment(previous.start_line, true);
+ }
} else if (has_comment(doc_comment_line, true)) {
if constexpr (std::is_same_v<T, ClassNode>) {
- get_class_doc_comment(doc_comment_line, member->doc_brief_description, member->doc_description, member->doc_tutorials, true);
+ member->doc_data = parse_class_doc_comment(doc_comment_line, true);
} else {
- member->doc_description = get_doc_comment(doc_comment_line);
+ member->doc_data = parse_doc_comment(doc_comment_line);
}
}
#endif // TOOLS_ENABLED
if (member->identifier != nullptr) {
if (!((String)member->identifier->name).is_empty()) { // Enums may be unnamed.
-
-#ifdef DEBUG_ENABLED
- List<MethodInfo> gdscript_funcs;
- GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
- for (MethodInfo &info : gdscript_funcs) {
- if (info.name == member->identifier->name) {
- push_warning(member->identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_member_kind, member->identifier->name, "built-in function");
- }
- }
- if (Variant::has_utility_function(member->identifier->name)) {
- push_warning(member->identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_member_kind, member->identifier->name, "built-in function");
- }
-#endif
-
if (current_class->members_indices.has(member->identifier->name)) {
push_error(vformat(R"(%s "%s" has the same name as a previously declared %s.)", p_member_kind.capitalize(), member->identifier->name, current_class->get_member(member->identifier->name).get_type_name()), member->identifier);
} else {
@@ -1139,6 +1129,7 @@ GDScriptParser::ConstantNode *GDScriptParser::parse_constant(bool p_is_static) {
ConstantNode *constant = alloc_node<ConstantNode>();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) {
+ complete_extents(constant);
return nullptr;
}
@@ -1327,25 +1318,34 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum(bool p_is_static) {
#ifdef TOOLS_ENABLED
// Enum values documentation.
for (int i = 0; i < enum_node->values.size(); i++) {
+ int doc_comment_line = enum_node->values[i].line;
+ bool single_line = false;
+
+ if (has_comment(doc_comment_line, true)) {
+ single_line = true;
+ } else if (has_comment(doc_comment_line - 1, true)) {
+ doc_comment_line--;
+ } else {
+ continue;
+ }
+
if (i == enum_node->values.size() - 1) {
// If close bracket is same line as last value.
- if (enum_node->values[i].line != previous.start_line && has_comment(enum_node->values[i].line)) {
- if (named) {
- enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true);
- } else {
- current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true));
- }
+ if (doc_comment_line == previous.start_line) {
+ break;
}
} else {
// If two values are same line.
- if (enum_node->values[i].line != enum_node->values[i + 1].line && has_comment(enum_node->values[i].line)) {
- if (named) {
- enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true);
- } else {
- current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true));
- }
+ if (doc_comment_line == enum_node->values[i + 1].line) {
+ continue;
}
}
+
+ if (named) {
+ enum_node->values.write[i].doc_data = parse_doc_comment(doc_comment_line, single_line);
+ } else {
+ current_class->set_enum_value_doc_data(enum_node->values[i].identifier->name, parse_doc_comment(doc_comment_line, single_line));
+ }
}
#endif // TOOLS_ENABLED
@@ -2147,6 +2147,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
ExpressionNode *expression = parse_expression(false);
if (expression == nullptr) {
push_error(R"(Expected expression for match pattern.)");
+ complete_extents(pattern);
return nullptr;
} else {
if (expression->type == GDScriptParser::Node::LITERAL) {
@@ -2279,6 +2280,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
IdentifierNode *identifier = alloc_node<IdentifierNode>();
complete_extents(identifier);
identifier->name = previous.get_identifier();
+#ifdef DEBUG_ENABLED
+ identifier->suite = current_suite;
+#endif
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
@@ -3229,7 +3233,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_type_test(ExpressionNode *
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_yield(ExpressionNode *p_previous_operand, bool p_can_assign) {
- push_error(R"("yield" was removed in Godot 4.0. Use "await" instead.)");
+ push_error(R"("yield" was removed in Godot 4. Use "await" instead.)");
return nullptr;
}
@@ -3423,19 +3427,20 @@ bool GDScriptParser::has_comment(int p_line, bool p_must_be_doc) {
return tokenizer.get_comments()[p_line].comment.begins_with("##");
}
-String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
+GDScriptParser::MemberDocData GDScriptParser::parse_doc_comment(int p_line, bool p_single_line) {
+ MemberDocData result;
+
const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
- ERR_FAIL_COND_V(!comments.has(p_line), String());
+ ERR_FAIL_COND_V(!comments.has(p_line), result);
if (p_single_line) {
if (comments[p_line].comment.begins_with("##")) {
- return comments[p_line].comment.trim_prefix("##").strip_edges();
+ result.description = comments[p_line].comment.trim_prefix("##").strip_edges();
+ return result;
}
- return "";
+ return result;
}
- String doc;
-
int line = p_line;
DocLineState state = DOC_LINE_NORMAL;
@@ -3463,29 +3468,42 @@ String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
}
String doc_line = comments[line].comment.trim_prefix("##");
- doc += _process_doc_line(doc_line, doc, space_prefix, state);
line++;
+
+ if (state == DOC_LINE_NORMAL) {
+ String stripped_line = doc_line.strip_edges();
+ if (stripped_line.begins_with("@deprecated")) {
+ result.is_deprecated = true;
+ continue;
+ } else if (stripped_line.begins_with("@experimental")) {
+ result.is_experimental = true;
+ continue;
+ }
+ }
+
+ result.description += _process_doc_line(doc_line, result.description, space_prefix, state);
}
- return doc;
+ return result;
}
-void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class) {
+GDScriptParser::ClassDocData GDScriptParser::parse_class_doc_comment(int p_line, bool p_inner_class, bool p_single_line) {
+ ClassDocData result;
+
const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
- if (!comments.has(p_line)) {
- return;
+ ERR_FAIL_COND_V(!comments.has(p_line), result);
+
+ if (p_single_line) {
+ if (comments[p_line].comment.begins_with("##")) {
+ result.brief = comments[p_line].comment.trim_prefix("##").strip_edges();
+ return result;
+ }
+ return result;
}
- ERR_FAIL_COND(!p_brief.is_empty() || !p_desc.is_empty() || p_tutorials.size() != 0);
int line = p_line;
DocLineState state = DOC_LINE_NORMAL;
- enum Mode {
- BRIEF,
- DESC,
- TUTORIALS,
- DONE,
- };
- Mode mode = BRIEF;
+ bool is_in_brief = true;
if (p_inner_class) {
while (comments.has(line - 1)) {
@@ -3512,18 +3530,21 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
break;
}
- String doc_line = comments[line++].comment.trim_prefix("##");
- String title, link; // For tutorials.
+ String doc_line = comments[line].comment.trim_prefix("##");
+ line++;
if (state == DOC_LINE_NORMAL) {
- // Set the read mode.
String stripped_line = doc_line.strip_edges();
- if (stripped_line.is_empty()) {
- if (mode == BRIEF && !p_brief.is_empty()) {
- mode = DESC;
- }
+
+ // A blank line separates the description from the brief.
+ if (is_in_brief && !result.brief.is_empty() && stripped_line.is_empty()) {
+ is_in_brief = false;
continue;
- } else if (stripped_line.begins_with("@tutorial")) {
+ }
+
+ if (stripped_line.begins_with("@tutorial")) {
+ String title, link;
+
int begin_scan = String("@tutorial").length();
if (begin_scan >= stripped_line.length()) {
continue; // Invalid syntax.
@@ -3565,24 +3586,21 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
link = stripped_line.substr(colon_pos).strip_edges();
}
- mode = TUTORIALS;
- } else if (mode == TUTORIALS) { // Tutorial docs are single line, we need a @tag after it.
- mode = DONE;
+ result.tutorials.append(Pair<String, String>(title, link));
+ continue;
+ } else if (stripped_line.begins_with("@deprecated")) {
+ result.is_deprecated = true;
+ continue;
+ } else if (stripped_line.begins_with("@experimental")) {
+ result.is_experimental = true;
+ continue;
}
}
- switch (mode) {
- case BRIEF:
- p_brief += _process_doc_line(doc_line, p_brief, space_prefix, state);
- break;
- case DESC:
- p_desc += _process_doc_line(doc_line, p_desc, space_prefix, state);
- break;
- case TUTORIALS:
- p_tutorials.append(Pair<String, String>(title, link));
- break;
- case DONE:
- break;
+ if (is_in_brief) {
+ result.brief += _process_doc_line(doc_line, result.brief, space_prefix, state);
+ } else {
+ result.description += _process_doc_line(doc_line, result.description, space_prefix, state);
}
}
@@ -3590,11 +3608,11 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
const ClassNode::Member &m = current_class->members[0];
int first_member_line = m.get_line();
if (first_member_line == line) {
- p_brief = "";
- p_desc = "";
- p_tutorials.clear();
+ result = ClassDocData(); // Clear result.
}
}
+
+ return result;
}
#endif // TOOLS_ENABLED
@@ -3879,6 +3897,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
}
+ // WARNING: Do not merge with the previous `if` because there `!=`, not `==`!
if (p_annotation->name == SNAME("@export_flags")) {
const int64_t max_flags = 32;
Vector<String> t = arg_string.split(":", true, 1);
@@ -3904,6 +3923,18 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Starting from argument %d, the flag value must be specified explicitly.)", i + 1, max_flags + 1), p_annotation->arguments[i]);
return false;
}
+ } else if (p_annotation->name == SNAME("@export_node_path")) {
+ String native_class = arg_string;
+ if (ScriptServer::is_global_class(arg_string)) {
+ native_class = ScriptServer::get_global_class_native_base(arg_string);
+ }
+ if (!ClassDB::class_exists(native_class)) {
+ push_error(vformat(R"(Invalid argument %d of annotation "@export_node_path": The class "%s" was not found in the global scope.)", i + 1, arg_string), p_annotation->arguments[i]);
+ return false;
+ } else if (!ClassDB::is_parent_class(native_class, SNAME("Node"))) {
+ push_error(vformat(R"(Invalid argument %d of annotation "@export_node_path": The class "%s" does not inherit "Node".)", i + 1, arg_string), p_annotation->arguments[i]);
+ return false;
+ }
}
if (i > 0) {
@@ -3921,8 +3952,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
if (export_type.builtin_type == Variant::INT) {
variable->export_info.type = Variant::INT;
}
- }
- if (p_annotation->name == SNAME("@export_multiline")) {
+ } else if (p_annotation->name == SNAME("@export_multiline")) {
if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
DataType inner_type = export_type.get_container_element_type();
if (inner_type.builtin_type != Variant::STRING) {
@@ -3950,6 +3980,8 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
}
+ // WARNING: Do not merge with the previous `else if`! Otherwise `else` (default variable type check)
+ // will not work for the above annotations. `@export` and `@export_enum` validate the type separately.
if (p_annotation->name == SNAME("@export")) {
if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 18757eb9fd..20f5dcf06d 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -257,6 +257,22 @@ public:
int line = 0, column = 0;
};
+#ifdef TOOLS_ENABLED
+ struct ClassDocData {
+ String brief;
+ String description;
+ Vector<Pair<String, String>> tutorials;
+ bool is_deprecated = false;
+ bool is_experimental = false;
+ };
+
+ struct MemberDocData {
+ String description;
+ bool is_deprecated = false;
+ bool is_experimental = false;
+ };
+#endif // TOOLS_ENABLED
+
struct Node {
enum Type {
NONE,
@@ -505,7 +521,7 @@ public:
int leftmost_column = 0;
int rightmost_column = 0;
#ifdef TOOLS_ENABLED
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
};
@@ -513,7 +529,7 @@ public:
Vector<Value> values;
Variant dictionary;
#ifdef TOOLS_ENABLED
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
EnumNode() {
@@ -720,14 +736,12 @@ public:
DataType base_type;
String fqcn; // Fully-qualified class name. Identifies uniquely any class in the project.
#ifdef TOOLS_ENABLED
- String doc_description;
- String doc_brief_description;
- Vector<Pair<String, String>> doc_tutorials;
+ ClassDocData doc_data;
// EnumValue docs are parsed after itself, so we need a method to add/modify the doc property later.
- void set_enum_value_doc(const StringName &p_name, const String &p_doc_description) {
+ void set_enum_value_doc_data(const StringName &p_name, const MemberDocData &p_doc_data) {
ERR_FAIL_INDEX(members_indices[p_name], members.size());
- members.write[members_indices[p_name]].enum_value.doc_description = p_doc_description;
+ members.write[members_indices[p_name]].enum_value.doc_data = p_doc_data;
}
#endif // TOOLS_ENABLED
@@ -753,7 +767,9 @@ public:
members.push_back(Member(p_enum_value));
}
void add_member_group(AnnotationNode *p_annotation_node) {
- members_indices[p_annotation_node->export_info.name] = members.size();
+ // Avoid name conflict. See GH-78252.
+ StringName name = vformat("@group_%d_%s", members.size(), p_annotation_node->export_info.name);
+ members_indices[name] = members.size();
members.push_back(Member(p_annotation_node));
}
@@ -764,7 +780,7 @@ public:
struct ConstantNode : public AssignableNode {
#ifdef TOOLS_ENABLED
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
ConstantNode() {
@@ -819,7 +835,7 @@ public:
LambdaNode *source_lambda = nullptr;
#ifdef TOOLS_ENABLED
Vector<Variant> default_arg_values;
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
bool resolved_signature = false;
@@ -843,19 +859,24 @@ public:
struct IdentifierNode : public ExpressionNode {
StringName name;
+#ifdef DEBUG_ENABLED
+ SuiteNode *suite = nullptr; // The block in which the identifier is used.
+#endif
enum Source {
UNDEFINED_SOURCE,
FUNCTION_PARAMETER,
- LOCAL_CONSTANT,
LOCAL_VARIABLE,
+ LOCAL_CONSTANT,
LOCAL_ITERATOR, // `for` loop iterator.
LOCAL_BIND, // Pattern bind.
- MEMBER_SIGNAL,
MEMBER_VARIABLE,
- STATIC_VARIABLE,
MEMBER_CONSTANT,
+ MEMBER_FUNCTION,
+ MEMBER_SIGNAL,
+ MEMBER_CLASS,
INHERITED_VARIABLE,
+ STATIC_VARIABLE,
};
Source source = UNDEFINED_SOURCE;
@@ -1006,7 +1027,7 @@ public:
Vector<ParameterNode *> parameters;
HashMap<StringName, int> parameters_indices;
#ifdef TOOLS_ENABLED
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
SignalNode() {
@@ -1211,7 +1232,7 @@ public:
int assignments = 0;
bool is_static = false;
#ifdef TOOLS_ENABLED
- String doc_description;
+ MemberDocData doc_data;
#endif // TOOLS_ENABLED
VariableNode() {
@@ -1486,12 +1507,12 @@ private:
ExpressionNode *parse_yield(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign);
TypeNode *parse_type(bool p_allow_void = false);
+
#ifdef TOOLS_ENABLED
- // Doc comments.
int class_doc_line = 0x7FFFFFFF;
bool has_comment(int p_line, bool p_must_be_doc = false);
- String get_doc_comment(int p_line, bool p_single_line = false);
- void get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class);
+ MemberDocData parse_doc_comment(int p_line, bool p_single_line = false);
+ ClassDocData parse_class_doc_comment(int p_line, bool p_inner_class, bool p_single_line = false);
#endif // TOOLS_ENABLED
public:
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 3927a4dd3e..4f374b63b0 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -579,6 +579,24 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
return make_identifier(name);
}
+ if (!only_ascii) {
+ // Kept here in case the order with push_error matters.
+ Token id = make_identifier(name);
+
+#ifdef DEBUG_ENABLED
+ // Additional checks for identifiers but only in debug and if it's available in TextServer.
+ if (TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY)) {
+ int64_t confusable = TS->is_confusable(name, keyword_list);
+ if (confusable >= 0) {
+ push_error(vformat(R"(Identifier "%s" is visually similar to the GDScript keyword "%s" and thus not allowed.)", name, keyword_list[confusable]));
+ }
+ }
+#endif // DEBUG_ENABLED
+
+ // Cannot be a keyword, as keywords are ASCII only.
+ return id;
+ }
+
// Define some helper macros for the switch case.
#define KEYWORD_GROUP_CASE(char) \
break; \
@@ -614,19 +632,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
}
// Not a keyword, so must be an identifier.
- Token id = make_identifier(name);
-
-#ifdef DEBUG_ENABLED
- // Additional checks for identifiers but only in debug and if it's available in TextServer.
- if (!only_ascii && TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY)) {
- int64_t confusable = TS->is_confusable(name, keyword_list);
- if (confusable >= 0) {
- push_error(vformat(R"(Identifier "%s" is visually similar to the GDScript keyword "%s" and thus not allowed.)", name, keyword_list[confusable]));
- }
- }
-#endif // DEBUG_ENABLED
-
- return id;
+ return make_identifier(name);
#undef KEYWORD_GROUP_CASE
#undef KEYWORD
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index a8c9cfa579..1ddd54b323 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -663,8 +663,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (GDScriptLanguage::get_singleton()->profiling) {
function_start_time = OS::get_singleton()->get_ticks_usec();
function_call_time = 0;
- profile.call_count++;
- profile.frame_call_count++;
+ profile.call_count.increment();
+ profile.frame_call_count.increment();
}
bool exit_ok = false;
bool awaited = false;
@@ -685,7 +685,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_SWITCH(_code_ptr[ip]) {
OPCODE(OPCODE_OPERATOR) {
- CHECK_SPACE(5);
+ constexpr int _pointer_size = sizeof(Variant::ValidatedOperatorEvaluator) / sizeof(*_code_ptr);
+ CHECK_SPACE(7 + _pointer_size);
bool valid;
Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4];
@@ -694,28 +695,71 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GET_VARIANT_PTR(a, 0);
GET_VARIANT_PTR(b, 1);
GET_VARIANT_PTR(dst, 2);
+ // Compute signatures (types of operands) so it can be optimized when matching.
+ uint32_t op_signature = _code_ptr[ip + 5];
+ uint32_t actual_signature = (a->get_type() << 8) | (b->get_type());
+
+ // Check if this is the first run. If so, store the current signature for the optimized path.
+ if (unlikely(op_signature == 0)) {
+ static Mutex initializer_mutex;
+ initializer_mutex.lock();
+ Variant::Type a_type = (Variant::Type)((actual_signature >> 8) & 0xFF);
+ Variant::Type b_type = (Variant::Type)(actual_signature & 0xFF);
+ Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(op, a_type, b_type);
+
+ if (unlikely(!op_func)) {
+#ifdef DEBUG_ENABLED
+ err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+#endif
+ initializer_mutex.unlock();
+ OPCODE_BREAK;
+ } else {
+ Variant::Type ret_type = Variant::get_operator_return_type(op, a_type, b_type);
+ VariantInternal::initialize(dst, ret_type);
+ op_func(a, b, dst);
+
+ // Check again in case another thread already set it.
+ if (_code_ptr[ip + 5] == 0) {
+ _code_ptr[ip + 5] = actual_signature;
+ _code_ptr[ip + 6] = static_cast<int>(ret_type);
+ Variant::ValidatedOperatorEvaluator *tmp = reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]);
+ *tmp = op_func;
+ }
+ }
+ initializer_mutex.unlock();
+ } else if (likely(op_signature == actual_signature)) {
+ // If the signature matches, we can use the optimized path.
+ Variant::Type ret_type = static_cast<Variant::Type>(_code_ptr[ip + 6]);
+ Variant::ValidatedOperatorEvaluator op_func = *reinterpret_cast<Variant::ValidatedOperatorEvaluator *>(&_code_ptr[ip + 7]);
+
+ // Make sure the return value has the correct type.
+ VariantInternal::initialize(dst, ret_type);
+ op_func(a, b, dst);
+ } else {
+ // If the signature doesn't match, we have to use the slow path.
#ifdef DEBUG_ENABLED
- Variant ret;
- Variant::evaluate(op, *a, *b, ret, valid);
+ Variant ret;
+ Variant::evaluate(op, *a, *b, ret, valid);
#else
- Variant::evaluate(op, *a, *b, *dst, valid);
+ Variant::evaluate(op, *a, *b, *dst, valid);
#endif
#ifdef DEBUG_ENABLED
- if (!valid) {
- if (ret.get_type() == Variant::STRING) {
- //return a string when invalid with the error
- err_text = ret;
- err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
- } else {
- err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+ if (!valid) {
+ if (ret.get_type() == Variant::STRING) {
+ //return a string when invalid with the error
+ err_text = ret;
+ err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
+ } else {
+ err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+ }
+ OPCODE_BREAK;
}
- OPCODE_BREAK;
- }
- *dst = ret;
+ *dst = ret;
#endif
- ip += 5;
+ }
+ ip += 7 + _pointer_size;
}
DISPATCH_OPCODE;
@@ -3550,7 +3594,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
// line
bool do_break = false;
- if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
+ if (unlikely(EngineDebugger::get_script_debugger()->get_lines_left() > 0)) {
if (EngineDebugger::get_script_debugger()->get_depth() <= 0) {
EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
}
@@ -3563,7 +3607,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
do_break = true;
}
- if (do_break) {
+ if (unlikely(do_break)) {
GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
}
@@ -3630,11 +3674,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->profiling) {
uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
- profile.total_time += time_taken;
- profile.self_time += time_taken - function_call_time;
- profile.frame_total_time += time_taken;
- profile.frame_self_time += time_taken - function_call_time;
- GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
+ profile.total_time.add(time_taken);
+ profile.self_time.add(time_taken - function_call_time);
+ profile.frame_total_time.add(time_taken);
+ profile.frame_self_time.add(time_taken - function_call_time);
+ if (Thread::get_caller_id() == Thread::get_main_id()) {
+ GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
+ }
}
// Check if this is not the last time it was interrupted by `await` or if it's the first time executing.
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 8de78d2b9a..24aa793c47 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -136,6 +136,12 @@ String GDScriptWarning::get_message() const {
case CONFUSABLE_IDENTIFIER:
CHECK_SYMBOLS(1);
return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]);
+ case CONFUSABLE_LOCAL_DECLARATION:
+ CHECK_SYMBOLS(2);
+ return vformat(R"(The %s "%s" is declared below in the parent block.)", symbols[0], symbols[1]);
+ case CONFUSABLE_LOCAL_USAGE:
+ CHECK_SYMBOLS(1);
+ return vformat(R"(The identifier "%s" will be shadowed below in the block.)", symbols[0]);
case INFERENCE_ON_VARIANT:
CHECK_SYMBOLS(1);
return vformat("The %s type is being inferred from a Variant value, so it will be typed as Variant.", symbols[0]);
@@ -213,6 +219,8 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"DEPRECATED_KEYWORD",
"RENAMED_IN_GODOT_4_HINT",
"CONFUSABLE_IDENTIFIER",
+ "CONFUSABLE_LOCAL_DECLARATION",
+ "CONFUSABLE_LOCAL_USAGE",
"INFERENCE_ON_VARIANT",
"NATIVE_METHOD_OVERRIDE",
"GET_NODE_DEFAULT_WITHOUT_ONREADY",
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index ae6207fcdc..8444d46a88 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -83,6 +83,8 @@ public:
DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
RENAMED_IN_GODOT_4_HINT, // A variable or function that could not be found has been renamed in Godot 4.
CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e").
+ CONFUSABLE_LOCAL_DECLARATION, // The parent block declares an identifier with the same name below.
+ CONFUSABLE_LOCAL_USAGE, // The identifier will be shadowed below in the block.
INFERENCE_ON_VARIANT, // The declaration uses type inference but the value is typed as Variant.
NATIVE_METHOD_OVERRIDE, // The script method overrides a native one, this may not work as intended.
GET_NODE_DEFAULT_WITHOUT_ONREADY, // A class variable uses `get_node()` (or the `$` notation) as its default value, but does not use the @onready annotation.
@@ -128,6 +130,8 @@ public:
WARN, // DEPRECATED_KEYWORD
WARN, // RENAMED_IN_GODOT_4_HINT
WARN, // CONFUSABLE_IDENTIFIER
+ WARN, // CONFUSABLE_LOCAL_DECLARATION
+ WARN, // CONFUSABLE_LOCAL_USAGE
ERROR, // INFERENCE_ON_VARIANT // Most likely done by accident, usually inference is trying for a particular type.
ERROR, // NATIVE_METHOD_OVERRIDE // May not work as expected.
ERROR, // GET_NODE_DEFAULT_WITHOUT_ONREADY // May not work as expected.
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/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
index 38c2faa859..8709b89b54 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
@@ -1,2 +1,2 @@
func test():
- CanvasItem.new()
+ InstancePlaceholder.new()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
index 9eff912b59..36224c6b6f 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Native class "CanvasItem" cannot be constructed as it is abstract.
+Native class "InstancePlaceholder" cannot be constructed as it is abstract.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
index 118e7e8a45..be67182efb 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
@@ -1,4 +1,4 @@
-class A extends CanvasItem:
+class A extends InstancePlaceholder:
func _init():
print('no')
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
index 8b956f5974..260f062555 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "CanvasItem".
+Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "InstancePlaceholder".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.gd
new file mode 100644
index 0000000000..57ae41922f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.gd
@@ -0,0 +1,4 @@
+var v1 = v1
+
+func test():
+ print(v1)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.out
new file mode 100644
index 0000000000..c337882d9c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var_self.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "v1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/allow_get_node_with_onready.gd b/modules/gdscript/tests/scripts/analyzer/features/allow_get_node_with_onready.gd
index a9004a346b..3e647407cd 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/allow_get_node_with_onready.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/allow_get_node_with_onready.gd
@@ -1,7 +1,7 @@
extends Node
@onready var shorthand = $Node
-@onready var call = get_node(^"Node")
+@onready var call_no_cast = get_node(^"Node")
@onready var shorthand_with_cast = $Node as Node
@onready var call_with_cast = get_node(^"Node") as Node
@@ -13,6 +13,6 @@ func _init():
func test():
# Those are expected to be `null` since `_ready()` is never called on tests.
prints("shorthand", shorthand)
- prints("call", call)
+ prints("call_no_cast", call_no_cast)
prints("shorthand_with_cast", shorthand_with_cast)
prints("call_with_cast", call_with_cast)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/allow_get_node_with_onready.out b/modules/gdscript/tests/scripts/analyzer/features/allow_get_node_with_onready.out
index eddc2deec0..78b830aad0 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/allow_get_node_with_onready.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/allow_get_node_with_onready.out
@@ -1,5 +1,5 @@
GDTEST_OK
shorthand <null>
-call <null>
+call_no_cast <null>
shorthand_with_cast <null>
call_with_cast <null>
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
index c4108f50de..b000c82717 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
@@ -126,7 +126,7 @@ func test():
assert(a_objects.get_typed_builtin() == TYPE_OBJECT)
assert(a_objects.get_typed_script() == A)
- var a_passed = (func check_a_passing(a_objects: Array[A]): return a_objects.size()).call(a_objects)
+ var a_passed = (func check_a_passing(p_objects: Array[A]): return p_objects.size()).call(a_objects)
assert(a_passed == 4)
var b_passed = (func check_b_passing(basic: Array): return basic[0] != null).call(b_objects)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.gd b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.gd
new file mode 100644
index 0000000000..3178f8d496
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.gd
@@ -0,0 +1,6 @@
+func test():
+ if true:
+ var a = 1
+ print(a)
+ var a = 2
+ print(a)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.out
new file mode 100644
index 0000000000..7365072ea7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_declaration.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+>> WARNING
+>> Line: 3
+>> CONFUSABLE_LOCAL_DECLARATION
+>> The variable "a" is declared below in the parent block.
+1
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.gd b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.gd
new file mode 100644
index 0000000000..4462c067bc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.gd
@@ -0,0 +1,6 @@
+var a = 1
+
+func test():
+ print(a)
+ var a = 2
+ print(a)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out
new file mode 100644
index 0000000000..0e0d607831
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage.out
@@ -0,0 +1,11 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> CONFUSABLE_LOCAL_USAGE
+>> The identifier "a" will be shadowed below in the block.
+>> WARNING
+>> Line: 5
+>> SHADOWED_VARIABLE
+>> The local variable "a" is shadowing an already-declared variable at line 1.
+1
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.gd b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.gd
new file mode 100644
index 0000000000..eef8eb66e6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.gd
@@ -0,0 +1,6 @@
+var a = 1
+
+func test():
+ print(a)
+ var a = a + 1
+ print(a)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out
new file mode 100644
index 0000000000..228a510490
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_initializer.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> CONFUSABLE_LOCAL_USAGE
+>> The identifier "a" will be shadowed below in the block.
+>> WARNING
+>> Line: 5
+>> CONFUSABLE_LOCAL_USAGE
+>> The identifier "a" will be shadowed below in the block.
+>> WARNING
+>> Line: 5
+>> SHADOWED_VARIABLE
+>> The local variable "a" is shadowing an already-declared variable at line 1.
+1
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.gd b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.gd
new file mode 100644
index 0000000000..1f207f27ac
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.gd
@@ -0,0 +1,7 @@
+var a = 1
+
+func test():
+ for _i in 3:
+ print(a)
+ var a = 2
+ print(a)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out
new file mode 100644
index 0000000000..0d20e9f7a0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_local_usage_loop.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> CONFUSABLE_LOCAL_USAGE
+>> The identifier "a" will be shadowed below in the block.
+>> WARNING
+>> Line: 6
+>> SHADOWED_VARIABLE
+>> The local variable "a" is shadowing an already-declared variable at line 1.
+1
+2
+1
+2
+1
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd
index 61945c9c8f..29239a19da 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.gd
@@ -1,5 +1,9 @@
var member: int = 0
+var print_debug := 'print_debug'
+@warning_ignore("shadowed_global_identifier")
+var print := 'print'
+
@warning_ignore("unused_variable")
func test():
var Array := 'Array'
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out
index 8467697a96..accc791d8a 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/shadowning.out
@@ -1,26 +1,30 @@
GDTEST_OK
>> WARNING
->> Line: 5
+>> Line: 3
+>> SHADOWED_GLOBAL_IDENTIFIER
+>> The variable "print_debug" has the same name as a built-in function.
+>> WARNING
+>> Line: 9
>> SHADOWED_GLOBAL_IDENTIFIER
>> The variable "Array" has the same name as a built-in type.
>> WARNING
->> Line: 6
+>> Line: 10
>> SHADOWED_GLOBAL_IDENTIFIER
>> The variable "Node" has the same name as a global class.
>> WARNING
->> Line: 7
+>> Line: 11
>> SHADOWED_GLOBAL_IDENTIFIER
>> The variable "is_same" has the same name as a built-in function.
>> WARNING
->> Line: 8
+>> Line: 12
>> SHADOWED_GLOBAL_IDENTIFIER
>> The variable "sqrt" has the same name as a built-in function.
>> WARNING
->> Line: 9
+>> Line: 13
>> SHADOWED_VARIABLE
>> The local variable "member" is shadowing an already-declared variable at line 1.
>> WARNING
->> Line: 10
+>> Line: 14
>> SHADOWED_VARIABLE_BASE_CLASS
>> The local variable "reference" is shadowing an already-declared method at the base class "RefCounted".
warn
diff --git a/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out b/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out
index 36cb699e92..f68d76d101 100644
--- a/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out
+++ b/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-"yield" was removed in Godot 4.0. Use "await" instead.
+"yield" was removed in Godot 4. Use "await" instead.
diff --git a/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.gd b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.gd
new file mode 100644
index 0000000000..e46f24cc5f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.gd
@@ -0,0 +1,17 @@
+extends RefCounted # TODO: Fix standalone annotations parsing.
+
+# GH-73843
+@export_group("Resource")
+
+# GH-78252
+@export var prop_1: int
+@export_category("prop_1")
+@export var prop_2: int
+
+func test():
+ var resource := Resource.new()
+ prints("Not shadowed:", resource.get_class())
+
+ for property in get_property_list():
+ if property.name in ["prop_1", "prop_2"]:
+ print(property)
diff --git a/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out
new file mode 100644
index 0000000000..96ae84e986
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/export_group_no_name_conflict_with_properties.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+Not shadowed: Resource
+{ "name": "prop_1", "class_name": &"", "type": 2, "hint": 0, "hint_string": "int", "usage": 4102 }
+{ "name": "prop_1", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 128 }
+{ "name": "prop_2", "class_name": &"", "type": 2, "hint": 0, "hint_string": "int", "usage": 4102 }
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index b5cf485286..7b862e6cca 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,28 +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_pbrSpecularGlossiness")) {
+ 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 (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();
@@ -3858,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);
@@ -3911,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);
@@ -3921,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);
@@ -3930,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);
@@ -3939,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);
@@ -3948,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);
}
@@ -7468,6 +7483,7 @@ Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> p_state) {
supported_extensions.insert("KHR_materials_pbrSpecularGlossiness");
supported_extensions.insert("KHR_texture_transform");
supported_extensions.insert("KHR_materials_unlit");
+ supported_extensions.insert("KHR_materials_emissive_strength");
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
Vector<String> ext_supported_extensions = ext->get_supported_extensions();
diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp
index 87149fa11d..1788ffac3a 100644
--- a/modules/gltf/register_types.cpp
+++ b/modules/gltf/register_types.cpp
@@ -70,9 +70,9 @@ static void _editor_init() {
if (blend_enabled) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (blender3_path.is_empty()) {
- WARN_PRINT("Blend file import is enabled in the project settings, but no Blender path is configured in the editor settings. Blend files will not be imported.");
+ WARN_PRINT(TTR("Blend file import is enabled in the project settings, but no Blender path is configured in the editor settings. Blend files will not be imported."));
} else if (!da->dir_exists(blender3_path)) {
- WARN_PRINT("Blend file import is enabled, but the Blender path doesn't point to an accessible directory. Blend files will not be imported.");
+ WARN_PRINT(TTR("Blend file import is enabled, but the Blender path doesn't point to an accessible directory. Blend files will not be imported."));
} else {
Ref<EditorSceneFormatImporterBlend> importer;
importer.instantiate();
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/minimp3/config.py b/modules/minimp3/config.py
index bd35d099b9..e6bdcb2a83 100644
--- a/modules/minimp3/config.py
+++ b/modules/minimp3/config.py
@@ -9,6 +9,7 @@ def configure(env):
def get_doc_classes():
return [
"AudioStreamMP3",
+ "ResourceImporterMP3",
]
diff --git a/modules/minimp3/doc_classes/ResourceImporterMP3.xml b/modules/minimp3/doc_classes/ResourceImporterMP3.xml
new file mode 100644
index 0000000000..a84c51cf68
--- /dev/null
+++ b/modules/minimp3/doc_classes/ResourceImporterMP3.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterMP3" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Imports a MP3 audio file for playback.
+ </brief_description>
+ <description>
+ MP3 is a lossy audio format, with worse audio quality compared to [ResourceImporterOggVorbis] at a given bitrate.
+ In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using a MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
+ MP3 requires more CPU to decode than [ResourceImporterWAV]. If you need to play a lot of simultaneous sounds, it's recommended to use WAV for those sounds instead, especially if targeting low-end devices.
+ </description>
+ <tutorials>
+ <link title="Importing audio samples">$DOCS_URL/tutorials/assets_pipeline/importing_audio_samples.html</link>
+ </tutorials>
+ <members>
+ <member name="bar_beats" type="int" setter="" getter="" default="4">
+ The number of bars within a single beat in the audio track. This is only relevant for music that wishes to make use of interactive music functionality (not implemented yet), not sound effects.
+ A more convenient editor for [member bar_beats] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
+ </member>
+ <member name="beat_count" type="int" setter="" getter="" default="0">
+ The beat count of the audio track. This is only relevant for music that wishes to make use of interactive music functionality (not implemented yet), not sound effects.
+ A more convenient editor for [member beat_count] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
+ </member>
+ <member name="bpm" type="float" setter="" getter="" default="0">
+ The Beats Per Minute of the audio track. This should match the BPM measure that was used to compose the track. This is only relevant for music that wishes to make use of interactive music functionality (not implemented yet), not sound effects.
+ A more convenient editor for [member bpm] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
+ </member>
+ <member name="loop" type="bool" setter="" getter="" default="false">
+ If enabled, the audio will begin playing at the beginning after playback ends by reaching the end of the audio.
+ [b]Note:[/b] In [AudioStreamPlayer], the [signal AudioStreamPlayer.finished] signal won't be emitted for looping audio when it reaches the end of the audio file, as the audio will keep playing indefinitely.
+ </member>
+ <member name="loop_offset" type="float" setter="" getter="" default="0">
+ Determines where audio will start to loop after playback reaches the end of the audio. This can be used to only loop a part of the audio file, which is useful for some ambient sounds or music. The value is determined in seconds relative to the beginning of the audio. A value of [code]0.0[/code] will loop the entire audio file.
+ Only has an effect if [member loop] is [code]true[/code].
+ A more convenient editor for [member loop_offset] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
+ </member>
+ </members>
+</class>
diff --git a/modules/minimp3/register_types.cpp b/modules/minimp3/register_types.cpp
index da89321018..627d093bc1 100644
--- a/modules/minimp3/register_types.cpp
+++ b/modules/minimp3/register_types.cpp
@@ -51,7 +51,11 @@ void initialize_minimp3_module(ModuleInitializationLevel p_level) {
mp3_import.instantiate();
ResourceFormatImporter::get_singleton()->add_importer(mp3_import);
}
+
+ // Required to document import options in the class reference.
+ GDREGISTER_CLASS(ResourceImporterMP3);
#endif
+
GDREGISTER_CLASS(AudioStreamMP3);
}
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index a4667f784d..564c5929c7 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -23,8 +23,6 @@ env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.cpp")
if env["platform"] in ["macos", "ios"]:
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.mm")
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.m")
-elif env["platform"] == "android":
- env_mono.add_source_files(env.modules_sources, "mono_gd/android_mono_config.gen.cpp")
if env.editor_build:
env_mono.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/mono/config.py b/modules/mono/config.py
index a36083b64b..2b2a8d6235 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -1,6 +1,6 @@
# Prior to .NET Core, we supported these: ["windows", "macos", "linuxbsd", "android", "haiku", "web", "ios"]
# Eventually support for each them should be added back (except Haiku if not supported by .NET Core)
-supported_platforms = ["windows", "macos", "linuxbsd"]
+supported_platforms = ["windows", "macos", "linuxbsd", "android"]
def can_build(env, platform):
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/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
index 1affe692d0..5ea0ca53c3 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
@@ -135,6 +135,10 @@ namespace Godot.SourceGenerators
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+ source.Append(" /// <summary>\n")
+ .Append(" /// Cached StringNames for the methods contained in this class, for fast lookup.\n")
+ .Append(" /// </summary>\n");
+
source.Append(
$" public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n");
@@ -147,6 +151,12 @@ namespace Godot.SourceGenerators
foreach (string methodName in distinctMethodNames)
{
+ source.Append(" /// <summary>\n")
+ .Append(" /// Cached name for the '")
+ .Append(methodName)
+ .Append("' method.\n")
+ .Append(" /// </summary>\n");
+
source.Append(" public new static readonly global::Godot.StringName ");
source.Append(methodName);
source.Append(" = \"");
@@ -162,6 +172,14 @@ namespace Godot.SourceGenerators
{
const string listType = "global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>";
+ source.Append(" /// <summary>\n")
+ .Append(" /// Get the method information for all the methods declared in this class.\n")
+ .Append(" /// This method is used by Godot to register the available methods in the editor.\n")
+ .Append(" /// Do not call this method.\n")
+ .Append(" /// </summary>\n");
+
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
+
source.Append(" internal new static ")
.Append(listType)
.Append(" GetGodotMethodList()\n {\n");
@@ -188,6 +206,8 @@ namespace Godot.SourceGenerators
if (godotClassMethods.Length > 0)
{
+ source.Append(" /// <inheritdoc/>\n");
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
source.Append(" protected override bool InvokeGodotClassMethod(in godot_string_name method, ");
source.Append("NativeVariantPtrArgs args, out godot_variant ret)\n {\n");
@@ -205,6 +225,8 @@ namespace Godot.SourceGenerators
if (distinctMethodNames.Length > 0)
{
+ source.Append(" /// <inheritdoc/>\n");
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
source.Append(" protected override bool HasGodotClassMethod(in godot_string_name method)\n {\n");
bool isFirstEntry = true;
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..94d8696717 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -124,6 +124,10 @@ namespace Godot.SourceGenerators
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+ source.Append(" /// <summary>\n")
+ .Append(" /// Cached StringNames for the properties and fields contained in this class, for fast lookup.\n")
+ .Append(" /// </summary>\n");
+
source.Append(
$" public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n");
@@ -132,6 +136,13 @@ namespace Godot.SourceGenerators
foreach (var property in godotClassProperties)
{
string propertyName = property.PropertySymbol.Name;
+
+ source.Append(" /// <summary>\n")
+ .Append(" /// Cached name for the '")
+ .Append(propertyName)
+ .Append("' property.\n")
+ .Append(" /// </summary>\n");
+
source.Append(" public new static readonly global::Godot.StringName ");
source.Append(propertyName);
source.Append(" = \"");
@@ -142,6 +153,13 @@ namespace Godot.SourceGenerators
foreach (var field in godotClassFields)
{
string fieldName = field.FieldSymbol.Name;
+
+ source.Append(" /// <summary>\n")
+ .Append(" /// Cached name for the '")
+ .Append(fieldName)
+ .Append("' field.\n")
+ .Append(" /// </summary>\n");
+
source.Append(" public new static readonly global::Godot.StringName ");
source.Append(fieldName);
source.Append(" = \"");
@@ -162,6 +180,8 @@ namespace Godot.SourceGenerators
if (!allPropertiesAreReadOnly)
{
+ source.Append(" /// <inheritdoc/>\n");
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
source.Append(" protected override bool SetGodotClassPropertyValue(in godot_string_name name, ");
source.Append("in godot_variant value)\n {\n");
@@ -193,6 +213,8 @@ namespace Godot.SourceGenerators
// Generate GetGodotClassPropertyValue
+ source.Append(" /// <inheritdoc/>\n");
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
source.Append(" protected override bool GetGodotClassPropertyValue(in godot_string_name name, ");
source.Append("out godot_variant value)\n {\n");
@@ -217,7 +239,15 @@ namespace Godot.SourceGenerators
// Generate GetGodotPropertyList
- string dictionaryType = "global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>";
+ const string dictionaryType = "global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>";
+
+ source.Append(" /// <summary>\n")
+ .Append(" /// Get the property information for all the properties declared in this class.\n")
+ .Append(" /// This method is used by Godot to register the available properties in the editor.\n")
+ .Append(" /// Do not call this method.\n")
+ .Append(" /// </summary>\n");
+
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
source.Append(" internal new static ")
.Append(dictionaryType)
@@ -620,7 +650,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/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
index ac908a6de3..4df16d05f0 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
@@ -285,10 +285,20 @@ namespace Godot.SourceGenerators
{
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
- string dictionaryType =
+ const string dictionaryType =
"global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>";
source.Append("#if TOOLS\n");
+
+ source.Append(" /// <summary>\n")
+ .Append(" /// Get the default values for all properties declared in this class.\n")
+ .Append(" /// This method is used by Godot to determine the value that will be\n")
+ .Append(" /// used by the inspector when resetting properties.\n")
+ .Append(" /// Do not call this method.\n")
+ .Append(" /// </summary>\n");
+
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
+
source.Append(" internal new static ");
source.Append(dictionaryType);
source.Append(" GetGodotPropertyDefaultValues()\n {\n");
@@ -320,7 +330,8 @@ namespace Godot.SourceGenerators
source.Append(" return values;\n");
source.Append(" }\n");
- source.Append("#endif\n");
+
+ source.Append("#endif // TOOLS\n");
source.Append("#pragma warning restore CS0109\n");
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
index 97771b721d..231a7be021 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
@@ -149,6 +149,8 @@ namespace Godot.SourceGenerators
godotSignalDelegates.Add(new(signalName, signalDelegateSymbol, invokeMethodData.Value));
}
+ source.Append(" /// <inheritdoc/>\n");
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
source.Append(
" protected override void SaveGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info)\n {\n");
source.Append(" base.SaveGodotObjectData(info);\n");
@@ -196,6 +198,8 @@ namespace Godot.SourceGenerators
source.Append(" }\n");
+ source.Append(" /// <inheritdoc/>\n");
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
source.Append(
" protected override void RestoreGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info)\n {\n");
source.Append(" base.RestoreGodotObjectData(info);\n");
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 7e3323f588..8f2774d5ae 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -176,6 +176,10 @@ namespace Godot.SourceGenerators
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+ source.Append(" /// <summary>\n")
+ .Append(" /// Cached StringNames for the signals contained in this class, for fast lookup.\n")
+ .Append(" /// </summary>\n");
+
source.Append(
$" public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n");
@@ -184,6 +188,13 @@ namespace Godot.SourceGenerators
foreach (var signalDelegate in godotSignalDelegates)
{
string signalName = signalDelegate.Name;
+
+ source.Append(" /// <summary>\n")
+ .Append(" /// Cached name for the '")
+ .Append(signalName)
+ .Append("' signal.\n")
+ .Append(" /// </summary>\n");
+
source.Append(" public new static readonly global::Godot.StringName ");
source.Append(signalName);
source.Append(" = \"");
@@ -199,6 +210,14 @@ namespace Godot.SourceGenerators
{
const string listType = "global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>";
+ source.Append(" /// <summary>\n")
+ .Append(" /// Get the signal information for all the signals declared in this class.\n")
+ .Append(" /// This method is used by Godot to register the available signals in the editor.\n")
+ .Append(" /// Do not call this method.\n")
+ .Append(" /// </summary>\n");
+
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
+
source.Append(" internal new static ")
.Append(listType)
.Append(" GetGodotSignalList()\n {\n");
@@ -258,6 +277,8 @@ namespace Godot.SourceGenerators
if (godotSignalDelegates.Count > 0)
{
+ source.Append(" /// <inheritdoc/>\n");
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
source.Append(
" protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, ");
source.Append("NativeVariantPtrArgs args)\n {\n");
@@ -276,6 +297,8 @@ namespace Godot.SourceGenerators
if (godotSignalDelegates.Count > 0)
{
+ source.Append(" /// <inheritdoc/>\n");
+ source.Append(" [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n");
source.Append(
" protected override bool HasGodotClassSignal(in godot_string_name signal)\n {\n");
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index ea2d14958b..4d61372ab0 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -3,6 +3,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
using GodotTools.Build;
using GodotTools.Core;
using GodotTools.Internals;
@@ -45,6 +47,17 @@ namespace GodotTools.Export
}
},
{ "default_value", true }
+ },
+ new Godot.Collections.Dictionary()
+ {
+ {
+ "option", new Godot.Collections.Dictionary()
+ {
+ { "name", "dotnet/embed_build_outputs" },
+ { "type", (int)Variant.Type.Bool }
+ }
+ },
+ { "default_value", false }
}
};
}
@@ -114,7 +127,7 @@ namespace GodotTools.Export
if (!DeterminePlatformFromFeatures(features, out string platform))
throw new NotSupportedException("Target platform not supported.");
- if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS }
+ if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS, OS.Platforms.Android }
.Contains(platform))
{
throw new NotImplementedException("Target platform not yet implemented.");
@@ -129,15 +142,19 @@ namespace GodotTools.Export
{
archs.Add("x86_64");
}
- else if (features.Contains("x86_32"))
+ if (features.Contains("x86_32"))
{
archs.Add("x86_32");
}
- else if (features.Contains("arm64"))
+ if (features.Contains("arm64"))
{
archs.Add("arm64");
}
- else if (features.Contains("universal"))
+ if (features.Contains("arm32"))
+ {
+ archs.Add("arm32");
+ }
+ if (features.Contains("universal"))
{
if (platform == OS.Platforms.MacOS)
{
@@ -146,6 +163,8 @@ namespace GodotTools.Export
}
}
+ bool embedBuildResults = (bool)GetOption("dotnet/embed_build_outputs") || features.Contains("android");
+
foreach (var arch in archs)
{
string ridOS = DetermineRuntimeIdentifierOS(platform);
@@ -190,17 +209,44 @@ namespace GodotTools.Export
"Publish succeeded but project assembly not found in the output directory");
}
- // Add to the exported project shared object list.
+ var manifest = new StringBuilder();
+ // Add to the exported project shared object list or packed resources.
foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
{
- AddSharedObject(file, tags: null,
- Path.Join(projectDataDirName,
- Path.GetRelativePath(publishOutputTempDir, Path.GetDirectoryName(file))));
+ if (embedBuildResults)
+ {
+ var filePath = SanitizeSlashes(Path.GetRelativePath(publishOutputTempDir, file));
+ var fileData = File.ReadAllBytes(file);
+ var hash = Convert.ToBase64String(SHA512.HashData(fileData));
+
+ manifest.Append($"{filePath}\t{hash}\n");
+
+ AddFile($"res://.godot/mono/publish/{arch}/{filePath}", fileData, false);
+ }
+ else
+ {
+ AddSharedObject(file, tags: null,
+ Path.Join(projectDataDirName,
+ Path.GetRelativePath(publishOutputTempDir, Path.GetDirectoryName(file))));
+ }
+ }
+
+ if (embedBuildResults)
+ {
+ var fileData = Encoding.Default.GetBytes(manifest.ToString());
+ AddFile($"res://.godot/mono/publish/{arch}/.dotnet-publish-manifest", fileData, false);
}
}
}
+ private string SanitizeSlashes(string path)
+ {
+ if (Path.DirectorySeparatorChar == '\\')
+ return path.Replace('\\', '/');
+ return path;
+ }
+
private string DetermineRuntimeIdentifierOS(string platform)
=> OS.DotNetOSPlatformMap[platform];
@@ -214,7 +260,7 @@ namespace GodotTools.Export
"x86_64" => "x64",
"armeabi-v7a" => "arm",
"arm64-v8a" => "arm64",
- "armv7" => "arm",
+ "arm32" => "arm",
"arm64" => "arm64",
_ => throw new ArgumentOutOfRangeException(nameof(arch), arch, "Unexpected architecture")
};
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 15bb574f4d..bed93cd69e 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -1616,7 +1616,16 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Generate InvokeGodotClassMethod
- output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
+ output << MEMBER_BEGIN "/// <summary>\n"
+ << INDENT1 "/// Invokes the method with the given name, using the given arguments.\n"
+ << INDENT1 "/// This method is used by Godot to invoke methods from the engine side.\n"
+ << INDENT1 "/// Do not call or override this method.\n"
+ << INDENT1 "/// </summary>\n"
+ << INDENT1 "/// <param name=\"method\">Name of the method to invoke.</param>\n"
+ << INDENT1 "/// <param name=\"args\">Arguments to use with the invoked method.</param>\n"
+ << INDENT1 "/// <param name=\"ret\">Value returned by the invoked method.</param>\n";
+
+ output << INDENT1 "protected internal " << (is_derived_type ? "override" : "virtual")
<< " bool " CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(in godot_string_name method, "
<< "NativeVariantPtrArgs args, out godot_variant ret)\n"
<< INDENT1 "{\n";
@@ -1696,6 +1705,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Generate HasGodotClassMethod
+ output << MEMBER_BEGIN "/// <summary>\n"
+ << INDENT1 "/// Check if the type contains a method with the given name.\n"
+ << INDENT1 "/// This method is used by Godot to check if a method exists before invoking it.\n"
+ << INDENT1 "/// Do not call or override this method.\n"
+ << INDENT1 "/// </summary>\n"
+ << INDENT1 "/// <param name=\"method\">Name of the method to check for.</param>\n";
+
output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
<< " bool " CS_METHOD_HAS_GODOT_CLASS_METHOD "(in godot_string_name method)\n"
<< INDENT1 "{\n";
@@ -1728,6 +1744,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Generate HasGodotClassSignal
+ output << MEMBER_BEGIN "/// <summary>\n"
+ << INDENT1 "/// Check if the type contains a signal with the given name.\n"
+ << INDENT1 "/// This method is used by Godot to check if a signal exists before raising it.\n"
+ << INDENT1 "/// Do not call or override this method.\n"
+ << INDENT1 "/// </summary>\n"
+ << INDENT1 "/// <param name=\"method\">Name of the method to check for.</param>\n";
+
output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
<< " bool " CS_METHOD_HAS_GODOT_CLASS_SIGNAL "(in godot_string_name signal)\n"
<< INDENT1 "{\n";
@@ -1758,39 +1781,57 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
//Generate StringName for all class members
bool is_inherit = !itype.is_singleton && obj_types.has(itype.base_name);
//PropertyName
+ output << MEMBER_BEGIN "/// <summary>\n"
+ << INDENT1 "/// Cached StringNames for the properties and fields contained in this class, for fast lookup.\n"
+ << INDENT1 "/// </summary>\n";
if (is_inherit) {
- output << MEMBER_BEGIN "public new class PropertyName : " << obj_types[itype.base_name].proxy_name << ".PropertyName";
+ output << INDENT1 "public new class PropertyName : " << obj_types[itype.base_name].proxy_name << ".PropertyName";
} else {
- output << MEMBER_BEGIN "public class PropertyName";
+ output << INDENT1 "public class PropertyName";
}
output << "\n"
<< INDENT1 "{\n";
for (const PropertyInterface &iprop : itype.properties) {
- output << INDENT2 "public static readonly StringName " << iprop.proxy_name << " = \"" << iprop.cname << "\";\n";
+ output << INDENT2 "/// <summary>\n"
+ << INDENT2 "/// Cached name for the '" << iprop.cname << "' property.\n"
+ << INDENT2 "/// </summary>\n"
+ << INDENT2 "public static readonly StringName " << iprop.proxy_name << " = \"" << iprop.cname << "\";\n";
}
output << INDENT1 "}\n";
//MethodName
+ output << MEMBER_BEGIN "/// <summary>\n"
+ << INDENT1 "/// Cached StringNames for the methods contained in this class, for fast lookup.\n"
+ << INDENT1 "/// </summary>\n";
if (is_inherit) {
- output << MEMBER_BEGIN "public new class MethodName : " << obj_types[itype.base_name].proxy_name << ".MethodName";
+ output << INDENT1 "public new class MethodName : " << obj_types[itype.base_name].proxy_name << ".MethodName";
} else {
- output << MEMBER_BEGIN "public class MethodName";
+ output << INDENT1 "public class MethodName";
}
output << "\n"
<< INDENT1 "{\n";
for (const MethodInterface &imethod : itype.methods) {
- output << INDENT2 "public static readonly StringName " << imethod.proxy_name << " = \"" << imethod.cname << "\";\n";
+ output << INDENT2 "/// <summary>\n"
+ << INDENT2 "/// Cached name for the '" << imethod.cname << "' method.\n"
+ << INDENT2 "/// </summary>\n"
+ << INDENT2 "public static readonly StringName " << imethod.proxy_name << " = \"" << imethod.cname << "\";\n";
}
output << INDENT1 "}\n";
//SignalName
+ output << MEMBER_BEGIN "/// <summary>\n"
+ << INDENT1 "/// Cached StringNames for the signals contained in this class, for fast lookup.\n"
+ << INDENT1 "/// </summary>\n";
if (is_inherit) {
- output << MEMBER_BEGIN "public new class SignalName : " << obj_types[itype.base_name].proxy_name << ".SignalName";
+ output << INDENT1 "public new class SignalName : " << obj_types[itype.base_name].proxy_name << ".SignalName";
} else {
- output << MEMBER_BEGIN "public class SignalName";
+ output << INDENT1 "public class SignalName";
}
output << "\n"
<< INDENT1 "{\n";
for (const SignalInterface &isignal : itype.signals_) {
- output << INDENT2 "public static readonly StringName " << isignal.proxy_name << " = \"" << isignal.cname << "\";\n";
+ output << INDENT2 "/// <summary>\n"
+ << INDENT2 "/// Cached name for the '" << isignal.cname << "' signal.\n"
+ << INDENT2 "/// </summary>\n"
+ << INDENT2 "public static readonly StringName " << isignal.proxy_name << " = \"" << isignal.cname << "\";\n";
}
output << INDENT1 "}\n";
@@ -2087,6 +2128,11 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
cs_in_expr_is_unsafe |= arg_type->cs_in_expr_is_unsafe;
}
+ // Collect caller name for MethodBind
+ if (p_imethod.is_vararg) {
+ icall_params += ", (godot_string_name)MethodName." + p_imethod.proxy_name + ".NativeValue";
+ }
+
// Generate method
{
if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
@@ -2460,6 +2506,11 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
i++;
}
+ // Collect caller name for MethodBind
+ if (p_icall.is_vararg) {
+ c_func_sig << ", godot_string_name caller";
+ }
+
String icall_method = p_icall.name;
// Generate icall function
@@ -2525,7 +2576,12 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
r_output << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_call("
<< CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
<< ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null")
- << ", total_length, out _);\n";
+ << ", total_length, out godot_variant_call_error vcall_error);\n";
+
+ r_output << base_indent << "ExceptionUtils.DebugCheckCallError(caller"
+ << ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
+ << ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null")
+ << ", total_length, vcall_error);\n";
if (!ret_void) {
if (return_type->cname != name_cache.type_Variant) {
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
index af83cc24bf..d25944dceb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Aabb.cs
@@ -564,7 +564,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if this <see cref="Aabb"/> is finite, by calling
- /// <see cref="Mathf.IsFinite"/> on each component.
+ /// <see cref="Mathf.IsFinite(real_t)"/> on each component.
/// </summary>
/// <returns>Whether this vector is finite or not.</returns>
public readonly bool IsFinite()
@@ -683,7 +683,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the AABB is exactly equal
- /// to the given object (<see paramref="obj"/>).
+ /// to the given object (<paramref name="obj"/>).
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 36f5d8e2ab..d53dd9a9af 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
{
@@ -595,7 +596,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if this basis is finite, by calling
- /// <see cref="Mathf.IsFinite"/> on each component.
+ /// <see cref="Mathf.IsFinite(real_t)"/> on each component.
/// </summary>
/// <returns>Whether this vector is finite or not.</returns>
public readonly bool IsFinite()
@@ -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).
@@ -1065,7 +1083,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the <see cref="Basis"/> is
- /// exactly equal to the given object (<see paramref="obj"/>).
+ /// exactly equal to the given object (<paramref name="obj"/>).
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
index 219a9a8c15..1239533a01 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
@@ -109,7 +109,8 @@ namespace Godot
}
godot_variant ret = NativeFuncs.godotsharp_callable_call(callable,
- (godot_variant**)argsPtr, argc, out _);
+ (godot_variant**)argsPtr, argc, out godot_variant_call_error vcall_error);
+ ExceptionUtils.DebugCheckCallError(callable, (godot_variant**)argsPtr, argc, vcall_error);
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
index 5bce66ea87..44b1c2554c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
@@ -10,301 +10,301 @@ namespace Godot
{
// Color names and values are derived from core/math/color_names.inc
internal static readonly Dictionary<string, Color> namedColors = new Dictionary<string, Color> {
- { "ALICEBLUE", new Color(0xF0F8FFFF) },
- { "ANTIQUEWHITE", new Color(0xFAEBD7FF) },
- { "AQUA", new Color(0x00FFFFFF) },
- { "AQUAMARINE", new Color(0x7FFFD4FF) },
- { "AZURE", new Color(0xF0FFFFFF) },
- { "BEIGE", new Color(0xF5F5DCFF) },
- { "BISQUE", new Color(0xFFE4C4FF) },
- { "BLACK", new Color(0x000000FF) },
- { "BLANCHEDALMOND", new Color(0xFFEBCDFF) },
- { "BLUE", new Color(0x0000FFFF) },
- { "BLUEVIOLET", new Color(0x8A2BE2FF) },
- { "BROWN", new Color(0xA52A2AFF) },
- { "BURLYWOOD", new Color(0xDEB887FF) },
- { "CADETBLUE", new Color(0x5F9EA0FF) },
- { "CHARTREUSE", new Color(0x7FFF00FF) },
- { "CHOCOLATE", new Color(0xD2691EFF) },
- { "CORAL", new Color(0xFF7F50FF) },
- { "CORNFLOWERBLUE", new Color(0x6495EDFF) },
- { "CORNSILK", new Color(0xFFF8DCFF) },
- { "CRIMSON", new Color(0xDC143CFF) },
- { "CYAN", new Color(0x00FFFFFF) },
- { "DARKBLUE", new Color(0x00008BFF) },
- { "DARKCYAN", new Color(0x008B8BFF) },
- { "DARKGOLDENROD", new Color(0xB8860BFF) },
- { "DARKGRAY", new Color(0xA9A9A9FF) },
- { "DARKGREEN", new Color(0x006400FF) },
- { "DARKKHAKI", new Color(0xBDB76BFF) },
- { "DARKMAGENTA", new Color(0x8B008BFF) },
- { "DARKOLIVEGREEN", new Color(0x556B2FFF) },
- { "DARKORANGE", new Color(0xFF8C00FF) },
- { "DARKORCHID", new Color(0x9932CCFF) },
- { "DARKRED", new Color(0x8B0000FF) },
- { "DARKSALMON", new Color(0xE9967AFF) },
- { "DARKSEAGREEN", new Color(0x8FBC8FFF) },
- { "DARKSLATEBLUE", new Color(0x483D8BFF) },
- { "DARKSLATEGRAY", new Color(0x2F4F4FFF) },
- { "DARKTURQUOISE", new Color(0x00CED1FF) },
- { "DARKVIOLET", new Color(0x9400D3FF) },
- { "DEEPPINK", new Color(0xFF1493FF) },
- { "DEEPSKYBLUE", new Color(0x00BFFFFF) },
- { "DIMGRAY", new Color(0x696969FF) },
- { "DODGERBLUE", new Color(0x1E90FFFF) },
- { "FIREBRICK", new Color(0xB22222FF) },
- { "FLORALWHITE", new Color(0xFFFAF0FF) },
- { "FORESTGREEN", new Color(0x228B22FF) },
- { "FUCHSIA", new Color(0xFF00FFFF) },
- { "GAINSBORO", new Color(0xDCDCDCFF) },
- { "GHOSTWHITE", new Color(0xF8F8FFFF) },
- { "GOLD", new Color(0xFFD700FF) },
- { "GOLDENROD", new Color(0xDAA520FF) },
- { "GRAY", new Color(0xBEBEBEFF) },
- { "GREEN", new Color(0x00FF00FF) },
- { "GREENYELLOW", new Color(0xADFF2FFF) },
- { "HONEYDEW", new Color(0xF0FFF0FF) },
- { "HOTPINK", new Color(0xFF69B4FF) },
- { "INDIANRED", new Color(0xCD5C5CFF) },
- { "INDIGO", new Color(0x4B0082FF) },
- { "IVORY", new Color(0xFFFFF0FF) },
- { "KHAKI", new Color(0xF0E68CFF) },
- { "LAVENDER", new Color(0xE6E6FAFF) },
- { "LAVENDERBLUSH", new Color(0xFFF0F5FF) },
- { "LAWNGREEN", new Color(0x7CFC00FF) },
- { "LEMONCHIFFON", new Color(0xFFFACDFF) },
- { "LIGHTBLUE", new Color(0xADD8E6FF) },
- { "LIGHTCORAL", new Color(0xF08080FF) },
- { "LIGHTCYAN", new Color(0xE0FFFFFF) },
- { "LIGHTGOLDENROD", new Color(0xFAFAD2FF) },
- { "LIGHTGRAY", new Color(0xD3D3D3FF) },
- { "LIGHTGREEN", new Color(0x90EE90FF) },
- { "LIGHTPINK", new Color(0xFFB6C1FF) },
- { "LIGHTSALMON", new Color(0xFFA07AFF) },
- { "LIGHTSEAGREEN", new Color(0x20B2AAFF) },
- { "LIGHTSKYBLUE", new Color(0x87CEFAFF) },
- { "LIGHTSLATEGRAY", new Color(0x778899FF) },
- { "LIGHTSTEELBLUE", new Color(0xB0C4DEFF) },
- { "LIGHTYELLOW", new Color(0xFFFFE0FF) },
- { "LIME", new Color(0x00FF00FF) },
- { "LIMEGREEN", new Color(0x32CD32FF) },
- { "LINEN", new Color(0xFAF0E6FF) },
- { "MAGENTA", new Color(0xFF00FFFF) },
- { "MAROON", new Color(0xB03060FF) },
- { "MEDIUMAQUAMARINE", new Color(0x66CDAAFF) },
- { "MEDIUMBLUE", new Color(0x0000CDFF) },
- { "MEDIUMORCHID", new Color(0xBA55D3FF) },
- { "MEDIUMPURPLE", new Color(0x9370DBFF) },
- { "MEDIUMSEAGREEN", new Color(0x3CB371FF) },
- { "MEDIUMSLATEBLUE", new Color(0x7B68EEFF) },
- { "MEDIUMSPRINGGREEN", new Color(0x00FA9AFF) },
- { "MEDIUMTURQUOISE", new Color(0x48D1CCFF) },
- { "MEDIUMVIOLETRED", new Color(0xC71585FF) },
- { "MIDNIGHTBLUE", new Color(0x191970FF) },
- { "MINTCREAM", new Color(0xF5FFFAFF) },
- { "MISTYROSE", new Color(0xFFE4E1FF) },
- { "MOCCASIN", new Color(0xFFE4B5FF) },
- { "NAVAJOWHITE", new Color(0xFFDEADFF) },
- { "NAVYBLUE", new Color(0x000080FF) },
- { "OLDLACE", new Color(0xFDF5E6FF) },
- { "OLIVE", new Color(0x808000FF) },
- { "OLIVEDRAB", new Color(0x6B8E23FF) },
- { "ORANGE", new Color(0xFFA500FF) },
- { "ORANGERED", new Color(0xFF4500FF) },
- { "ORCHID", new Color(0xDA70D6FF) },
- { "PALEGOLDENROD", new Color(0xEEE8AAFF) },
- { "PALEGREEN", new Color(0x98FB98FF) },
- { "PALETURQUOISE", new Color(0xAFEEEEFF) },
- { "PALEVIOLETRED", new Color(0xDB7093FF) },
- { "PAPAYAWHIP", new Color(0xFFEFD5FF) },
- { "PEACHPUFF", new Color(0xFFDAB9FF) },
- { "PERU", new Color(0xCD853FFF) },
- { "PINK", new Color(0xFFC0CBFF) },
- { "PLUM", new Color(0xDDA0DDFF) },
- { "POWDERBLUE", new Color(0xB0E0E6FF) },
- { "PURPLE", new Color(0xA020F0FF) },
- { "REBECCAPURPLE", new Color(0x663399FF) },
- { "RED", new Color(0xFF0000FF) },
- { "ROSYBROWN", new Color(0xBC8F8FFF) },
- { "ROYALBLUE", new Color(0x4169E1FF) },
- { "SADDLEBROWN", new Color(0x8B4513FF) },
- { "SALMON", new Color(0xFA8072FF) },
- { "SANDYBROWN", new Color(0xF4A460FF) },
- { "SEAGREEN", new Color(0x2E8B57FF) },
- { "SEASHELL", new Color(0xFFF5EEFF) },
- { "SIENNA", new Color(0xA0522DFF) },
- { "SILVER", new Color(0xC0C0C0FF) },
- { "SKYBLUE", new Color(0x87CEEBFF) },
- { "SLATEBLUE", new Color(0x6A5ACDFF) },
- { "SLATEGRAY", new Color(0x708090FF) },
- { "SNOW", new Color(0xFFFAFAFF) },
- { "SPRINGGREEN", new Color(0x00FF7FFF) },
- { "STEELBLUE", new Color(0x4682B4FF) },
- { "TAN", new Color(0xD2B48CFF) },
- { "TEAL", new Color(0x008080FF) },
- { "THISTLE", new Color(0xD8BFD8FF) },
- { "TOMATO", new Color(0xFF6347FF) },
- { "TRANSPARENT", new Color(0xFFFFFF00) },
- { "TURQUOISE", new Color(0x40E0D0FF) },
- { "VIOLET", new Color(0xEE82EEFF) },
- { "WEBGRAY", new Color(0x808080FF) },
- { "WEBGREEN", new Color(0x008000FF) },
- { "WEBMAROON", new Color(0x800000FF) },
- { "WEBPURPLE", new Color(0x800080FF) },
- { "WHEAT", new Color(0xF5DEB3FF) },
- { "WHITE", new Color(0xFFFFFFFF) },
- { "WHITESMOKE", new Color(0xF5F5F5FF) },
- { "YELLOW", new Color(0xFFFF00FF) },
- { "YELLOWGREEN", new Color(0x9ACD32FF) },
+ { "ALICEBLUE", Colors.AliceBlue },
+ { "ANTIQUEWHITE", Colors.AntiqueWhite },
+ { "AQUA", Colors.Aqua },
+ { "AQUAMARINE", Colors.Aquamarine },
+ { "AZURE", Colors.Azure },
+ { "BEIGE", Colors.Beige },
+ { "BISQUE", Colors.Bisque },
+ { "BLACK", Colors.Black },
+ { "BLANCHEDALMOND", Colors.BlanchedAlmond },
+ { "BLUE", Colors.Blue },
+ { "BLUEVIOLET", Colors.BlueViolet },
+ { "BROWN", Colors.Brown },
+ { "BURLYWOOD", Colors.Burlywood },
+ { "CADETBLUE", Colors.CadetBlue },
+ { "CHARTREUSE", Colors.Chartreuse },
+ { "CHOCOLATE", Colors.Chocolate },
+ { "CORAL", Colors.Coral },
+ { "CORNFLOWERBLUE", Colors.CornflowerBlue },
+ { "CORNSILK", Colors.Cornsilk },
+ { "CRIMSON", Colors.Crimson },
+ { "CYAN", Colors.Cyan },
+ { "DARKBLUE", Colors.DarkBlue },
+ { "DARKCYAN", Colors.DarkCyan },
+ { "DARKGOLDENROD", Colors.DarkGoldenrod },
+ { "DARKGRAY", Colors.DarkGray },
+ { "DARKGREEN", Colors.DarkGreen },
+ { "DARKKHAKI", Colors.DarkKhaki },
+ { "DARKMAGENTA", Colors.DarkMagenta },
+ { "DARKOLIVEGREEN", Colors.DarkOliveGreen },
+ { "DARKORANGE", Colors.DarkOrange },
+ { "DARKORCHID", Colors.DarkOrchid },
+ { "DARKRED", Colors.DarkRed },
+ { "DARKSALMON", Colors.DarkSalmon },
+ { "DARKSEAGREEN", Colors.DarkSeaGreen },
+ { "DARKSLATEBLUE", Colors.DarkSlateBlue },
+ { "DARKSLATEGRAY", Colors.DarkSlateGray },
+ { "DARKTURQUOISE", Colors.DarkTurquoise },
+ { "DARKVIOLET", Colors.DarkViolet },
+ { "DEEPPINK", Colors.DeepPink },
+ { "DEEPSKYBLUE", Colors.DeepSkyBlue },
+ { "DIMGRAY", Colors.DimGray },
+ { "DODGERBLUE", Colors.DodgerBlue },
+ { "FIREBRICK", Colors.Firebrick },
+ { "FLORALWHITE", Colors.FloralWhite },
+ { "FORESTGREEN", Colors.ForestGreen },
+ { "FUCHSIA", Colors.Fuchsia },
+ { "GAINSBORO", Colors.Gainsboro },
+ { "GHOSTWHITE", Colors.GhostWhite },
+ { "GOLD", Colors.Gold },
+ { "GOLDENROD", Colors.Goldenrod },
+ { "GRAY", Colors.Gray },
+ { "GREEN", Colors.Green },
+ { "GREENYELLOW", Colors.GreenYellow },
+ { "HONEYDEW", Colors.Honeydew },
+ { "HOTPINK", Colors.HotPink },
+ { "INDIANRED", Colors.IndianRed },
+ { "INDIGO", Colors.Indigo },
+ { "IVORY", Colors.Ivory },
+ { "KHAKI", Colors.Khaki },
+ { "LAVENDER", Colors.Lavender },
+ { "LAVENDERBLUSH", Colors.LavenderBlush },
+ { "LAWNGREEN", Colors.LawnGreen },
+ { "LEMONCHIFFON", Colors.LemonChiffon },
+ { "LIGHTBLUE", Colors.LightBlue },
+ { "LIGHTCORAL", Colors.LightCoral },
+ { "LIGHTCYAN", Colors.LightCyan },
+ { "LIGHTGOLDENROD", Colors.LightGoldenrod },
+ { "LIGHTGRAY", Colors.LightGray },
+ { "LIGHTGREEN", Colors.LightGreen },
+ { "LIGHTPINK", Colors.LightPink },
+ { "LIGHTSALMON", Colors.LightSalmon },
+ { "LIGHTSEAGREEN", Colors.LightSeaGreen },
+ { "LIGHTSKYBLUE", Colors.LightSkyBlue },
+ { "LIGHTSLATEGRAY", Colors.LightSlateGray },
+ { "LIGHTSTEELBLUE", Colors.LightSteelBlue },
+ { "LIGHTYELLOW", Colors.LightYellow },
+ { "LIME", Colors.Lime },
+ { "LIMEGREEN", Colors.LimeGreen },
+ { "LINEN", Colors.Linen },
+ { "MAGENTA", Colors.Magenta },
+ { "MAROON", Colors.Maroon },
+ { "MEDIUMAQUAMARINE", Colors.MediumAquamarine },
+ { "MEDIUMBLUE", Colors.MediumBlue },
+ { "MEDIUMORCHID", Colors.MediumOrchid },
+ { "MEDIUMPURPLE", Colors.MediumPurple },
+ { "MEDIUMSEAGREEN", Colors.MediumSeaGreen },
+ { "MEDIUMSLATEBLUE", Colors.MediumSlateBlue },
+ { "MEDIUMSPRINGGREEN", Colors.MediumSpringGreen },
+ { "MEDIUMTURQUOISE", Colors.MediumTurquoise },
+ { "MEDIUMVIOLETRED", Colors.MediumVioletRed },
+ { "MIDNIGHTBLUE", Colors.MidnightBlue },
+ { "MINTCREAM", Colors.MintCream },
+ { "MISTYROSE", Colors.MistyRose },
+ { "MOCCASIN", Colors.Moccasin },
+ { "NAVAJOWHITE", Colors.NavajoWhite },
+ { "NAVYBLUE", Colors.NavyBlue },
+ { "OLDLACE", Colors.OldLace },
+ { "OLIVE", Colors.Olive },
+ { "OLIVEDRAB", Colors.OliveDrab },
+ { "ORANGE", Colors.Orange },
+ { "ORANGERED", Colors.OrangeRed },
+ { "ORCHID", Colors.Orchid },
+ { "PALEGOLDENROD", Colors.PaleGoldenrod },
+ { "PALEGREEN", Colors.PaleGreen },
+ { "PALETURQUOISE", Colors.PaleTurquoise },
+ { "PALEVIOLETRED", Colors.PaleVioletRed },
+ { "PAPAYAWHIP", Colors.PapayaWhip },
+ { "PEACHPUFF", Colors.PeachPuff },
+ { "PERU", Colors.Peru },
+ { "PINK", Colors.Pink },
+ { "PLUM", Colors.Plum },
+ { "POWDERBLUE", Colors.PowderBlue },
+ { "PURPLE", Colors.Purple },
+ { "REBECCAPURPLE", Colors.RebeccaPurple },
+ { "RED", Colors.Red },
+ { "ROSYBROWN", Colors.RosyBrown },
+ { "ROYALBLUE", Colors.RoyalBlue },
+ { "SADDLEBROWN", Colors.SaddleBrown },
+ { "SALMON", Colors.Salmon },
+ { "SANDYBROWN", Colors.SandyBrown },
+ { "SEAGREEN", Colors.SeaGreen },
+ { "SEASHELL", Colors.Seashell },
+ { "SIENNA", Colors.Sienna },
+ { "SILVER", Colors.Silver },
+ { "SKYBLUE", Colors.SkyBlue },
+ { "SLATEBLUE", Colors.SlateBlue },
+ { "SLATEGRAY", Colors.SlateGray },
+ { "SNOW", Colors.Snow },
+ { "SPRINGGREEN", Colors.SpringGreen },
+ { "STEELBLUE", Colors.SteelBlue },
+ { "TAN", Colors.Tan },
+ { "TEAL", Colors.Teal },
+ { "THISTLE", Colors.Thistle },
+ { "TOMATO", Colors.Tomato },
+ { "TRANSPARENT", Colors.Transparent },
+ { "TURQUOISE", Colors.Turquoise },
+ { "VIOLET", Colors.Violet },
+ { "WEBGRAY", Colors.WebGray },
+ { "WEBGREEN", Colors.WebGreen },
+ { "WEBMAROON", Colors.WebMaroon },
+ { "WEBPURPLE", Colors.WebPurple },
+ { "WHEAT", Colors.Wheat },
+ { "WHITE", Colors.White },
+ { "WHITESMOKE", Colors.WhiteSmoke },
+ { "YELLOW", Colors.Yellow },
+ { "YELLOWGREEN", Colors.YellowGreen },
};
#pragma warning disable CS1591 // Disable warning: "Missing XML comment for publicly visible type or member"
- public static Color AliceBlue { get { return namedColors["ALICEBLUE"]; } }
- public static Color AntiqueWhite { get { return namedColors["ANTIQUEWHITE"]; } }
- public static Color Aqua { get { return namedColors["AQUA"]; } }
- public static Color Aquamarine { get { return namedColors["AQUAMARINE"]; } }
- public static Color Azure { get { return namedColors["AZURE"]; } }
- public static Color Beige { get { return namedColors["BEIGE"]; } }
- public static Color Bisque { get { return namedColors["BISQUE"]; } }
- public static Color Black { get { return namedColors["BLACK"]; } }
- public static Color BlanchedAlmond { get { return namedColors["BLANCHEDALMOND"]; } }
- public static Color Blue { get { return namedColors["BLUE"]; } }
- public static Color BlueViolet { get { return namedColors["BLUEVIOLET"]; } }
- public static Color Brown { get { return namedColors["BROWN"]; } }
- public static Color Burlywood { get { return namedColors["BURLYWOOD"]; } }
- public static Color CadetBlue { get { return namedColors["CADETBLUE"]; } }
- public static Color Chartreuse { get { return namedColors["CHARTREUSE"]; } }
- public static Color Chocolate { get { return namedColors["CHOCOLATE"]; } }
- public static Color Coral { get { return namedColors["CORAL"]; } }
- public static Color CornflowerBlue { get { return namedColors["CORNFLOWERBLUE"]; } }
- public static Color Cornsilk { get { return namedColors["CORNSILK"]; } }
- public static Color Crimson { get { return namedColors["CRIMSON"]; } }
- public static Color Cyan { get { return namedColors["CYAN"]; } }
- public static Color DarkBlue { get { return namedColors["DARKBLUE"]; } }
- public static Color DarkCyan { get { return namedColors["DARKCYAN"]; } }
- public static Color DarkGoldenrod { get { return namedColors["DARKGOLDENROD"]; } }
- public static Color DarkGray { get { return namedColors["DARKGRAY"]; } }
- public static Color DarkGreen { get { return namedColors["DARKGREEN"]; } }
- public static Color DarkKhaki { get { return namedColors["DARKKHAKI"]; } }
- public static Color DarkMagenta { get { return namedColors["DARKMAGENTA"]; } }
- public static Color DarkOliveGreen { get { return namedColors["DARKOLIVEGREEN"]; } }
- public static Color DarkOrange { get { return namedColors["DARKORANGE"]; } }
- public static Color DarkOrchid { get { return namedColors["DARKORCHID"]; } }
- public static Color DarkRed { get { return namedColors["DARKRED"]; } }
- public static Color DarkSalmon { get { return namedColors["DARKSALMON"]; } }
- public static Color DarkSeaGreen { get { return namedColors["DARKSEAGREEN"]; } }
- public static Color DarkSlateBlue { get { return namedColors["DARKSLATEBLUE"]; } }
- public static Color DarkSlateGray { get { return namedColors["DARKSLATEGRAY"]; } }
- public static Color DarkTurquoise { get { return namedColors["DARKTURQUOISE"]; } }
- public static Color DarkViolet { get { return namedColors["DARKVIOLET"]; } }
- public static Color DeepPink { get { return namedColors["DEEPPINK"]; } }
- public static Color DeepSkyBlue { get { return namedColors["DEEPSKYBLUE"]; } }
- public static Color DimGray { get { return namedColors["DIMGRAY"]; } }
- public static Color DodgerBlue { get { return namedColors["DODGERBLUE"]; } }
- public static Color Firebrick { get { return namedColors["FIREBRICK"]; } }
- public static Color FloralWhite { get { return namedColors["FLORALWHITE"]; } }
- public static Color ForestGreen { get { return namedColors["FORESTGREEN"]; } }
- public static Color Fuchsia { get { return namedColors["FUCHSIA"]; } }
- public static Color Gainsboro { get { return namedColors["GAINSBORO"]; } }
- public static Color GhostWhite { get { return namedColors["GHOSTWHITE"]; } }
- public static Color Gold { get { return namedColors["GOLD"]; } }
- public static Color Goldenrod { get { return namedColors["GOLDENROD"]; } }
- public static Color Gray { get { return namedColors["GRAY"]; } }
- public static Color Green { get { return namedColors["GREEN"]; } }
- public static Color GreenYellow { get { return namedColors["GREENYELLOW"]; } }
- public static Color Honeydew { get { return namedColors["HONEYDEW"]; } }
- public static Color HotPink { get { return namedColors["HOTPINK"]; } }
- public static Color IndianRed { get { return namedColors["INDIANRED"]; } }
- public static Color Indigo { get { return namedColors["INDIGO"]; } }
- public static Color Ivory { get { return namedColors["IVORY"]; } }
- public static Color Khaki { get { return namedColors["KHAKI"]; } }
- public static Color Lavender { get { return namedColors["LAVENDER"]; } }
- public static Color LavenderBlush { get { return namedColors["LAVENDERBLUSH"]; } }
- public static Color LawnGreen { get { return namedColors["LAWNGREEN"]; } }
- public static Color LemonChiffon { get { return namedColors["LEMONCHIFFON"]; } }
- public static Color LightBlue { get { return namedColors["LIGHTBLUE"]; } }
- public static Color LightCoral { get { return namedColors["LIGHTCORAL"]; } }
- public static Color LightCyan { get { return namedColors["LIGHTCYAN"]; } }
- public static Color LightGoldenrod { get { return namedColors["LIGHTGOLDENROD"]; } }
- public static Color LightGray { get { return namedColors["LIGHTGRAY"]; } }
- public static Color LightGreen { get { return namedColors["LIGHTGREEN"]; } }
- public static Color LightPink { get { return namedColors["LIGHTPINK"]; } }
- public static Color LightSalmon { get { return namedColors["LIGHTSALMON"]; } }
- public static Color LightSeaGreen { get { return namedColors["LIGHTSEAGREEN"]; } }
- public static Color LightSkyBlue { get { return namedColors["LIGHTSKYBLUE"]; } }
- public static Color LightSlateGray { get { return namedColors["LIGHTSLATEGRAY"]; } }
- public static Color LightSteelBlue { get { return namedColors["LIGHTSTEELBLUE"]; } }
- public static Color LightYellow { get { return namedColors["LIGHTYELLOW"]; } }
- public static Color Lime { get { return namedColors["LIME"]; } }
- public static Color LimeGreen { get { return namedColors["LIMEGREEN"]; } }
- public static Color Linen { get { return namedColors["LINEN"]; } }
- public static Color Magenta { get { return namedColors["MAGENTA"]; } }
- public static Color Maroon { get { return namedColors["MAROON"]; } }
- public static Color MediumAquamarine { get { return namedColors["MEDIUMAQUAMARINE"]; } }
- public static Color MediumBlue { get { return namedColors["MEDIUMBLUE"]; } }
- public static Color MediumOrchid { get { return namedColors["MEDIUMORCHID"]; } }
- public static Color MediumPurple { get { return namedColors["MEDIUMPURPLE"]; } }
- public static Color MediumSeaGreen { get { return namedColors["MEDIUMSEAGREEN"]; } }
- public static Color MediumSlateBlue { get { return namedColors["MEDIUMSLATEBLUE"]; } }
- public static Color MediumSpringGreen { get { return namedColors["MEDIUMSPRINGGREEN"]; } }
- public static Color MediumTurquoise { get { return namedColors["MEDIUMTURQUOISE"]; } }
- public static Color MediumVioletRed { get { return namedColors["MEDIUMVIOLETRED"]; } }
- public static Color MidnightBlue { get { return namedColors["MIDNIGHTBLUE"]; } }
- public static Color MintCream { get { return namedColors["MINTCREAM"]; } }
- public static Color MistyRose { get { return namedColors["MISTYROSE"]; } }
- public static Color Moccasin { get { return namedColors["MOCCASIN"]; } }
- public static Color NavajoWhite { get { return namedColors["NAVAJOWHITE"]; } }
- public static Color NavyBlue { get { return namedColors["NAVYBLUE"]; } }
- public static Color OldLace { get { return namedColors["OLDLACE"]; } }
- public static Color Olive { get { return namedColors["OLIVE"]; } }
- public static Color OliveDrab { get { return namedColors["OLIVEDRAB"]; } }
- public static Color Orange { get { return namedColors["ORANGE"]; } }
- public static Color OrangeRed { get { return namedColors["ORANGERED"]; } }
- public static Color Orchid { get { return namedColors["ORCHID"]; } }
- public static Color PaleGoldenrod { get { return namedColors["PALEGOLDENROD"]; } }
- public static Color PaleGreen { get { return namedColors["PALEGREEN"]; } }
- public static Color PaleTurquoise { get { return namedColors["PALETURQUOISE"]; } }
- public static Color PaleVioletRed { get { return namedColors["PALEVIOLETRED"]; } }
- public static Color PapayaWhip { get { return namedColors["PAPAYAWHIP"]; } }
- public static Color PeachPuff { get { return namedColors["PEACHPUFF"]; } }
- public static Color Peru { get { return namedColors["PERU"]; } }
- public static Color Pink { get { return namedColors["PINK"]; } }
- public static Color Plum { get { return namedColors["PLUM"]; } }
- public static Color PowderBlue { get { return namedColors["POWDERBLUE"]; } }
- public static Color Purple { get { return namedColors["PURPLE"]; } }
- public static Color RebeccaPurple { get { return namedColors["REBECCAPURPLE"]; } }
- public static Color Red { get { return namedColors["RED"]; } }
- public static Color RosyBrown { get { return namedColors["ROSYBROWN"]; } }
- public static Color RoyalBlue { get { return namedColors["ROYALBLUE"]; } }
- public static Color SaddleBrown { get { return namedColors["SADDLEBROWN"]; } }
- public static Color Salmon { get { return namedColors["SALMON"]; } }
- public static Color SandyBrown { get { return namedColors["SANDYBROWN"]; } }
- public static Color SeaGreen { get { return namedColors["SEAGREEN"]; } }
- public static Color Seashell { get { return namedColors["SEASHELL"]; } }
- public static Color Sienna { get { return namedColors["SIENNA"]; } }
- public static Color Silver { get { return namedColors["SILVER"]; } }
- public static Color SkyBlue { get { return namedColors["SKYBLUE"]; } }
- public static Color SlateBlue { get { return namedColors["SLATEBLUE"]; } }
- public static Color SlateGray { get { return namedColors["SLATEGRAY"]; } }
- public static Color Snow { get { return namedColors["SNOW"]; } }
- public static Color SpringGreen { get { return namedColors["SPRINGGREEN"]; } }
- public static Color SteelBlue { get { return namedColors["STEELBLUE"]; } }
- public static Color Tan { get { return namedColors["TAN"]; } }
- public static Color Teal { get { return namedColors["TEAL"]; } }
- public static Color Thistle { get { return namedColors["THISTLE"]; } }
- public static Color Tomato { get { return namedColors["TOMATO"]; } }
- public static Color Transparent { get { return namedColors["TRANSPARENT"]; } }
- public static Color Turquoise { get { return namedColors["TURQUOISE"]; } }
- public static Color Violet { get { return namedColors["VIOLET"]; } }
- public static Color WebGray { get { return namedColors["WEBGRAY"]; } }
- public static Color WebGreen { get { return namedColors["WEBGREEN"]; } }
- public static Color WebMaroon { get { return namedColors["WEBMAROON"]; } }
- public static Color WebPurple { get { return namedColors["WEBPURPLE"]; } }
- public static Color Wheat { get { return namedColors["WHEAT"]; } }
- public static Color White { get { return namedColors["WHITE"]; } }
- public static Color WhiteSmoke { get { return namedColors["WHITESMOKE"]; } }
- public static Color Yellow { get { return namedColors["YELLOW"]; } }
- public static Color YellowGreen { get { return namedColors["YELLOWGREEN"]; } }
+ public static Color AliceBlue => new Color(0xF0F8FFFF);
+ public static Color AntiqueWhite => new Color(0xFAEBD7FF);
+ public static Color Aqua => new Color(0x00FFFFFF);
+ public static Color Aquamarine => new Color(0x7FFFD4FF);
+ public static Color Azure => new Color(0xF0FFFFFF);
+ public static Color Beige => new Color(0xF5F5DCFF);
+ public static Color Bisque => new Color(0xFFE4C4FF);
+ public static Color Black => new Color(0x000000FF);
+ public static Color BlanchedAlmond => new Color(0xFFEBCDFF);
+ public static Color Blue => new Color(0x0000FFFF);
+ public static Color BlueViolet => new Color(0x8A2BE2FF);
+ public static Color Brown => new Color(0xA52A2AFF);
+ public static Color Burlywood => new Color(0xDEB887FF);
+ public static Color CadetBlue => new Color(0x5F9EA0FF);
+ public static Color Chartreuse => new Color(0x7FFF00FF);
+ public static Color Chocolate => new Color(0xD2691EFF);
+ public static Color Coral => new Color(0xFF7F50FF);
+ public static Color CornflowerBlue => new Color(0x6495EDFF);
+ public static Color Cornsilk => new Color(0xFFF8DCFF);
+ public static Color Crimson => new Color(0xDC143CFF);
+ public static Color Cyan => new Color(0x00FFFFFF);
+ public static Color DarkBlue => new Color(0x00008BFF);
+ public static Color DarkCyan => new Color(0x008B8BFF);
+ public static Color DarkGoldenrod => new Color(0xB8860BFF);
+ public static Color DarkGray => new Color(0xA9A9A9FF);
+ public static Color DarkGreen => new Color(0x006400FF);
+ public static Color DarkKhaki => new Color(0xBDB76BFF);
+ public static Color DarkMagenta => new Color(0x8B008BFF);
+ public static Color DarkOliveGreen => new Color(0x556B2FFF);
+ public static Color DarkOrange => new Color(0xFF8C00FF);
+ public static Color DarkOrchid => new Color(0x9932CCFF);
+ public static Color DarkRed => new Color(0x8B0000FF);
+ public static Color DarkSalmon => new Color(0xE9967AFF);
+ public static Color DarkSeaGreen => new Color(0x8FBC8FFF);
+ public static Color DarkSlateBlue => new Color(0x483D8BFF);
+ public static Color DarkSlateGray => new Color(0x2F4F4FFF);
+ public static Color DarkTurquoise => new Color(0x00CED1FF);
+ public static Color DarkViolet => new Color(0x9400D3FF);
+ public static Color DeepPink => new Color(0xFF1493FF);
+ public static Color DeepSkyBlue => new Color(0x00BFFFFF);
+ public static Color DimGray => new Color(0x696969FF);
+ public static Color DodgerBlue => new Color(0x1E90FFFF);
+ public static Color Firebrick => new Color(0xB22222FF);
+ public static Color FloralWhite => new Color(0xFFFAF0FF);
+ public static Color ForestGreen => new Color(0x228B22FF);
+ public static Color Fuchsia => new Color(0xFF00FFFF);
+ public static Color Gainsboro => new Color(0xDCDCDCFF);
+ public static Color GhostWhite => new Color(0xF8F8FFFF);
+ public static Color Gold => new Color(0xFFD700FF);
+ public static Color Goldenrod => new Color(0xDAA520FF);
+ public static Color Gray => new Color(0xBEBEBEFF);
+ public static Color Green => new Color(0x00FF00FF);
+ public static Color GreenYellow => new Color(0xADFF2FFF);
+ public static Color Honeydew => new Color(0xF0FFF0FF);
+ public static Color HotPink => new Color(0xFF69B4FF);
+ public static Color IndianRed => new Color(0xCD5C5CFF);
+ public static Color Indigo => new Color(0x4B0082FF);
+ public static Color Ivory => new Color(0xFFFFF0FF);
+ public static Color Khaki => new Color(0xF0E68CFF);
+ public static Color Lavender => new Color(0xE6E6FAFF);
+ public static Color LavenderBlush => new Color(0xFFF0F5FF);
+ public static Color LawnGreen => new Color(0x7CFC00FF);
+ public static Color LemonChiffon => new Color(0xFFFACDFF);
+ public static Color LightBlue => new Color(0xADD8E6FF);
+ public static Color LightCoral => new Color(0xF08080FF);
+ public static Color LightCyan => new Color(0xE0FFFFFF);
+ public static Color LightGoldenrod => new Color(0xFAFAD2FF);
+ public static Color LightGray => new Color(0xD3D3D3FF);
+ public static Color LightGreen => new Color(0x90EE90FF);
+ public static Color LightPink => new Color(0xFFB6C1FF);
+ public static Color LightSalmon => new Color(0xFFA07AFF);
+ public static Color LightSeaGreen => new Color(0x20B2AAFF);
+ public static Color LightSkyBlue => new Color(0x87CEFAFF);
+ public static Color LightSlateGray => new Color(0x778899FF);
+ public static Color LightSteelBlue => new Color(0xB0C4DEFF);
+ public static Color LightYellow => new Color(0xFFFFE0FF);
+ public static Color Lime => new Color(0x00FF00FF);
+ public static Color LimeGreen => new Color(0x32CD32FF);
+ public static Color Linen => new Color(0xFAF0E6FF);
+ public static Color Magenta => new Color(0xFF00FFFF);
+ public static Color Maroon => new Color(0xB03060FF);
+ public static Color MediumAquamarine => new Color(0x66CDAAFF);
+ public static Color MediumBlue => new Color(0x0000CDFF);
+ public static Color MediumOrchid => new Color(0xBA55D3FF);
+ public static Color MediumPurple => new Color(0x9370DBFF);
+ public static Color MediumSeaGreen => new Color(0x3CB371FF);
+ public static Color MediumSlateBlue => new Color(0x7B68EEFF);
+ public static Color MediumSpringGreen => new Color(0x00FA9AFF);
+ public static Color MediumTurquoise => new Color(0x48D1CCFF);
+ public static Color MediumVioletRed => new Color(0xC71585FF);
+ public static Color MidnightBlue => new Color(0x191970FF);
+ public static Color MintCream => new Color(0xF5FFFAFF);
+ public static Color MistyRose => new Color(0xFFE4E1FF);
+ public static Color Moccasin => new Color(0xFFE4B5FF);
+ public static Color NavajoWhite => new Color(0xFFDEADFF);
+ public static Color NavyBlue => new Color(0x000080FF);
+ public static Color OldLace => new Color(0xFDF5E6FF);
+ public static Color Olive => new Color(0x808000FF);
+ public static Color OliveDrab => new Color(0x6B8E23FF);
+ public static Color Orange => new Color(0xFFA500FF);
+ public static Color OrangeRed => new Color(0xFF4500FF);
+ public static Color Orchid => new Color(0xDA70D6FF);
+ public static Color PaleGoldenrod => new Color(0xEEE8AAFF);
+ public static Color PaleGreen => new Color(0x98FB98FF);
+ public static Color PaleTurquoise => new Color(0xAFEEEEFF);
+ public static Color PaleVioletRed => new Color(0xDB7093FF);
+ public static Color PapayaWhip => new Color(0xFFEFD5FF);
+ public static Color PeachPuff => new Color(0xFFDAB9FF);
+ public static Color Peru => new Color(0xCD853FFF);
+ public static Color Pink => new Color(0xFFC0CBFF);
+ public static Color Plum => new Color(0xDDA0DDFF);
+ public static Color PowderBlue => new Color(0xB0E0E6FF);
+ public static Color Purple => new Color(0xA020F0FF);
+ public static Color RebeccaPurple => new Color(0x663399FF);
+ public static Color Red => new Color(0xFF0000FF);
+ public static Color RosyBrown => new Color(0xBC8F8FFF);
+ public static Color RoyalBlue => new Color(0x4169E1FF);
+ public static Color SaddleBrown => new Color(0x8B4513FF);
+ public static Color Salmon => new Color(0xFA8072FF);
+ public static Color SandyBrown => new Color(0xF4A460FF);
+ public static Color SeaGreen => new Color(0x2E8B57FF);
+ public static Color Seashell => new Color(0xFFF5EEFF);
+ public static Color Sienna => new Color(0xA0522DFF);
+ public static Color Silver => new Color(0xC0C0C0FF);
+ public static Color SkyBlue => new Color(0x87CEEBFF);
+ public static Color SlateBlue => new Color(0x6A5ACDFF);
+ public static Color SlateGray => new Color(0x708090FF);
+ public static Color Snow => new Color(0xFFFAFAFF);
+ public static Color SpringGreen => new Color(0x00FF7FFF);
+ public static Color SteelBlue => new Color(0x4682B4FF);
+ public static Color Tan => new Color(0xD2B48CFF);
+ public static Color Teal => new Color(0x008080FF);
+ public static Color Thistle => new Color(0xD8BFD8FF);
+ public static Color Tomato => new Color(0xFF6347FF);
+ public static Color Transparent => new Color(0xFFFFFF00);
+ public static Color Turquoise => new Color(0x40E0D0FF);
+ public static Color Violet => new Color(0xEE82EEFF);
+ public static Color WebGray => new Color(0x808080FF);
+ public static Color WebGreen => new Color(0x008000FF);
+ public static Color WebMaroon => new Color(0x800000FF);
+ public static Color WebPurple => new Color(0x800080FF);
+ public static Color Wheat => new Color(0xF5DEB3FF);
+ public static Color White => new Color(0xFFFFFFFF);
+ public static Color WhiteSmoke => new Color(0xF5F5F5FF);
+ public static Color Yellow => new Color(0xFFFF00FF);
+ public static Color YellowGreen => new Color(0x9ACD32FF);
#pragma warning restore CS1591
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index 5d03379430..6c2fb7374c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -500,24 +500,17 @@ namespace Godot
Type? returnType = hasReturn ? DeserializeType(reader) : typeof(void);
int parametersCount = reader.ReadInt32();
+ var parameterTypes = parametersCount == 0 ? Type.EmptyTypes : new Type[parametersCount];
- if (parametersCount > 0)
+ for (int i = 0; i < parametersCount; i++)
{
- var parameterTypes = new Type[parametersCount];
-
- for (int i = 0; i < parametersCount; i++)
- {
- Type? parameterType = DeserializeType(reader);
- if (parameterType == null)
- return false;
- parameterTypes[i] = parameterType;
- }
-
- methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags, null, parameterTypes, null);
- return methodInfo != null && methodInfo.ReturnType == returnType;
+ Type? parameterType = DeserializeType(reader);
+ if (parameterType == null)
+ return false;
+ parameterTypes[i] = parameterType;
}
- methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags);
+ methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags, null, parameterTypes, null);
return methodInfo != null && methodInfo.ReturnType == returnType;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
index 12e8a638d3..c6337e56ef 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
@@ -191,12 +191,30 @@ namespace Godot
}
// ReSharper disable once VirtualMemberNeverOverridden.Global
+ /// <summary>
+ /// Set the value of a property contained in this class.
+ /// This method is used by Godot to assign property values.
+ /// Do not call or override this method.
+ /// </summary>
+ /// <param name="name">Name of the property to set.</param>
+ /// <param name="value">Value to set the property to if it was found.</param>
+ /// <returns><see langword="true"/> if a property with the given name was found.</returns>
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
{
return false;
}
// ReSharper disable once VirtualMemberNeverOverridden.Global
+ /// <summary>
+ /// Get the value of a property contained in this class.
+ /// This method is used by Godot to retrieve property values.
+ /// Do not call or override this method.
+ /// </summary>
+ /// <param name="name">Name of the property to get.</param>
+ /// <param name="value">Value of the property if it was found.</param>
+ /// <returns><see langword="true"/> if a property with the given name was found.</returns>
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected internal virtual bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
{
value = default;
@@ -204,6 +222,14 @@ namespace Godot
}
// ReSharper disable once VirtualMemberNeverOverridden.Global
+ /// <summary>
+ /// Raises the signal with the given name, using the given arguments.
+ /// This method is used by Godot to raise signals from the engine side.\n"
+ /// Do not call or override this method.
+ /// </summary>
+ /// <param name="signal">Name of the signal to raise.</param>
+ /// <param name="args">Arguments to use with the raised signal.</param>
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected internal virtual void RaiseGodotClassSignalCallbacks(in godot_string_name signal,
NativeVariantPtrArgs args)
{
@@ -233,11 +259,25 @@ namespace Godot
return nativeConstructor;
}
+ /// <summary>
+ /// Saves this instance's state to be restored when reloading assemblies.
+ /// Do not call or override this method.
+ /// To add data to be saved and restored, implement <see cref="ISerializationListener"/>.
+ /// </summary>
+ /// <param name="info">Object used to save the data.</param>
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected internal virtual void SaveGodotObjectData(GodotSerializationInfo info)
{
}
// TODO: Should this be a constructor overload?
+ /// <summary>
+ /// Restores this instance's state after reloading assemblies.
+ /// Do not call or override this method.
+ /// To add data to be saved and restored, implement <see cref="ISerializationListener"/>.
+ /// </summary>
+ /// <param name="info">Object that contains the previously saved data.</param>
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected internal virtual void RestoreGodotObjectData(GodotSerializationInfo info)
{
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
index 90b4d1b8d3..3288705dab 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
@@ -1,11 +1,21 @@
namespace Godot
{
/// <summary>
- /// An interface that requires methods for before and after serialization.
+ /// Allows a GodotObject to react to the serialization/deserialization
+ /// that occurs when Godot reloads assemblies.
/// </summary>
public interface ISerializationListener
{
+ /// <summary>
+ /// Executed before serializing this instance's state when reloading assemblies.
+ /// Clear any data that should not be serialized.
+ /// </summary>
void OnBeforeSerialize();
+
+ /// <summary>
+ /// Executed after deserializing this instance's state after reloading assemblies.
+ /// Restore any state that has been lost.
+ /// </summary>
void OnAfterDeserialize();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
index 2d8067d300..ba2c232580 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
@@ -135,5 +135,109 @@ namespace Godot.NativeInterop
OnExceptionLoggerException(unexpected, e);
}
}
+
+ [Conditional("DEBUG")]
+ public unsafe static void DebugCheckCallError(godot_string_name method, IntPtr instance, godot_variant** args, int argCount, godot_variant_call_error error)
+ {
+ if (error.Error != godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_OK)
+ {
+ using godot_variant instanceVariant = VariantUtils.CreateFromGodotObjectPtr(instance);
+ string where = GetCallErrorWhere(method, &instanceVariant, args, argCount);
+ string errorText = GetCallErrorMessage(error, where, args);
+ GD.PushError(errorText);
+ }
+ }
+
+ [Conditional("DEBUG")]
+ public unsafe static void DebugCheckCallError(in godot_callable callable, godot_variant** args, int argCount, godot_variant_call_error error)
+ {
+ if (error.Error != godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_OK)
+ {
+ using godot_variant callableVariant = VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue(callable);
+ string where = $"callable '{VariantUtils.ConvertToString(callableVariant)}'";
+ string errorText = GetCallErrorMessage(error, where, args);
+ GD.PushError(errorText);
+ }
+ }
+
+ private unsafe static string GetCallErrorWhere(godot_string_name method, godot_variant* instance, godot_variant** args, int argCount)
+ {
+ string? methodstr = null;
+ string basestr = GetVariantTypeName(instance);
+
+ if (method == GodotObject.MethodName.Call || (basestr == "Godot.TreeItem" && method == TreeItem.MethodName.CallRecursive))
+ {
+ if (argCount >= 1)
+ {
+ methodstr = VariantUtils.ConvertToString(*args[0]);
+ }
+ }
+
+ if (string.IsNullOrEmpty(methodstr))
+ {
+ methodstr = StringName.CreateTakingOwnershipOfDisposableValue(method);
+ }
+
+ return $"function '{methodstr}' in base '{basestr}'";
+ }
+
+ private unsafe static string GetCallErrorMessage(godot_variant_call_error error, string where, godot_variant** args)
+ {
+ switch (error.Error)
+ {
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT:
+ {
+ int errorarg = error.Argument;
+ // Handle the Object to Object case separately as we don't have further class details.
+#if DEBUG
+ if (error.Expected == Variant.Type.Object && args[errorarg]->Type == error.Expected)
+ {
+ return $"Invalid type in {where}. The Object-derived class of argument {errorarg + 1} (" + GetVariantTypeName(args[errorarg]) + ") is not a subclass of the expected argument class.";
+ }
+ else if (error.Expected == Variant.Type.Array && args[errorarg]->Type == error.Expected)
+ {
+ return $"Invalid type in {where}. The array of argument {errorarg + 1} (" + GetVariantTypeName(args[errorarg]) + ") does not have the same element type as the expected typed array argument.";
+ }
+ else
+#endif
+ {
+ return $"Invalid type in {where}. Cannot convert argument {errorarg + 1} from {args[errorarg]->Type} to {error.Expected}.";
+ }
+ }
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS:
+ return $"Invalid call to {where}. Expected {error.Argument} arguments.";
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD:
+ return $"Invalid call. Nonexistent {where}.";
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL:
+ return $"Attempt to call {where} on a null instance.";
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_METHOD_NOT_CONST:
+ return $"Attempt to call {where} on a const instance.";
+ default:
+ return $"Bug, call error: #{error.Error}";
+ }
+ }
+
+ private unsafe static string GetVariantTypeName(godot_variant* variant)
+ {
+ if (variant->Type == Variant.Type.Object)
+ {
+ GodotObject obj = VariantUtils.ConvertToGodotObject(*variant);
+ if (obj == null)
+ {
+ return "null instance";
+ }
+ else if (!GodotObject.IsInstanceValid(obj))
+ {
+ return "previously freed";
+ }
+ else
+ {
+ return obj.GetType().ToString();
+ }
+ }
+
+ return variant->Type.ToString();
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
index 43e7c7eb9a..1dddc82e85 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
@@ -71,6 +71,7 @@ namespace Godot.NativeInterop
GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS,
GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS,
GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL,
+ GODOT_CALL_ERROR_CALL_ERROR_METHOD_NOT_CONST,
}
[StructLayout(LayoutKind.Sequential)]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index 55b7a83fc2..3c7455a76c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -204,7 +204,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if this plane is finite, by calling
- /// <see cref="Mathf.IsFinite"/> on each component.
+ /// <see cref="Mathf.IsFinite(real_t)"/> on each component.
/// </summary>
/// <returns>Whether this vector is finite or not.</returns>
public readonly bool IsFinite()
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
index 84fc73b87a..998a2786a7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
@@ -976,7 +976,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the projection is exactly equal
- /// to the given object (<see paramref="obj"/>).
+ /// to the given object (<paramref name="obj"/>).
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
index 9c2a6fc654..2e282447bd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
@@ -103,7 +103,7 @@ namespace Godot
///
/// Note: This method has an abnormally high amount
/// of floating-point error, so methods such as
- /// <see cref="Mathf.IsZeroApprox"/> will not work reliably.
+ /// <see cref="Mathf.IsZeroApprox(real_t)"/> will not work reliably.
/// </summary>
/// <param name="to">The other quaternion.</param>
/// <returns>The angle between the quaternions.</returns>
@@ -320,7 +320,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if this quaternion is finite, by calling
- /// <see cref="Mathf.IsFinite"/> on each component.
+ /// <see cref="Mathf.IsFinite(real_t)"/> on each component.
/// </summary>
/// <returns>Whether this vector is finite or not.</returns>
public readonly bool IsFinite()
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index 69444f8035..458802f95d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -102,7 +102,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if this <see cref="Rect2"/> is finite, by calling
- /// <see cref="Mathf.IsFinite"/> on each component.
+ /// <see cref="Mathf.IsFinite(real_t)"/> on each component.
/// </summary>
/// <returns>Whether this vector is finite or not.</returns>
public bool IsFinite()
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index d7392dbda8..618c892681 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -232,7 +232,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if this transform is finite, by calling
- /// <see cref="Mathf.IsFinite"/> on each component.
+ /// <see cref="Mathf.IsFinite(real_t)"/> on each component.
/// </summary>
/// <returns>Whether this vector is finite or not.</returns>
public readonly bool IsFinite()
@@ -586,7 +586,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the transform is exactly equal
- /// to the given object (<see paramref="obj"/>).
+ /// to the given object (<paramref name="obj"/>).
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 1e2aaa299f..b16e6e592e 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
{
@@ -155,7 +156,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if this transform is finite, by calling
- /// <see cref="Mathf.IsFinite"/> on each component.
+ /// <see cref="Mathf.IsFinite(real_t)"/> on each component.
/// </summary>
/// <returns>Whether this vector is finite or not.</returns>
public readonly bool IsFinite()
@@ -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;
}
@@ -600,7 +613,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the transform is exactly equal
- /// to the given object (<see paramref="obj"/>).
+ /// to the given object (<paramref name="obj"/>).
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 0bf8f25f06..642ef231f3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -164,7 +164,7 @@ namespace Godot
/// <summary>
/// Returns a new vector with all components rounded up (towards positive infinity).
/// </summary>
- /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
+ /// <returns>A vector with <see cref="Mathf.Ceil(real_t)"/> called on each component.</returns>
public readonly Vector2 Ceil()
{
return new Vector2(Mathf.Ceil(X), Mathf.Ceil(Y));
@@ -318,7 +318,7 @@ namespace Godot
/// <summary>
/// Returns a new vector with all components rounded down (towards negative infinity).
/// </summary>
- /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
+ /// <returns>A vector with <see cref="Mathf.Floor(real_t)"/> called on each component.</returns>
public readonly Vector2 Floor()
{
return new Vector2(Mathf.Floor(X), Mathf.Floor(Y));
@@ -335,7 +335,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if this vector is finite, by calling
- /// <see cref="Mathf.IsFinite"/> on each component.
+ /// <see cref="Mathf.IsFinite(real_t)"/> on each component.
/// </summary>
/// <returns>Whether this vector is finite or not.</returns>
public readonly bool IsFinite()
@@ -948,7 +948,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the vector is exactly equal
- /// to the given object (<see paramref="obj"/>).
+ /// to the given object (<paramref name="obj"/>).
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
index 0dac8205b6..231e791904 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
@@ -517,7 +517,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the vector is equal
- /// to the given object (<see paramref="obj"/>).
+ /// to the given object (<paramref name="obj"/>).
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index c773c0fda6..7d548f1d10 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -150,7 +150,7 @@ namespace Godot
/// <summary>
/// Returns a new vector with all components rounded up (towards positive infinity).
/// </summary>
- /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
+ /// <returns>A vector with <see cref="Mathf.Ceil(real_t)"/> called on each component.</returns>
public readonly Vector3 Ceil()
{
return new Vector3(Mathf.Ceil(X), Mathf.Ceil(Y), Mathf.Ceil(Z));
@@ -315,7 +315,7 @@ namespace Godot
/// <summary>
/// Returns a new vector with all components rounded down (towards negative infinity).
/// </summary>
- /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
+ /// <returns>A vector with <see cref="Mathf.Floor(real_t)"/> called on each component.</returns>
public readonly Vector3 Floor()
{
return new Vector3(Mathf.Floor(X), Mathf.Floor(Y), Mathf.Floor(Z));
@@ -332,7 +332,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if this vector is finite, by calling
- /// <see cref="Mathf.IsFinite"/> on each component.
+ /// <see cref="Mathf.IsFinite(real_t)"/> on each component.
/// </summary>
/// <returns>Whether this vector is finite or not.</returns>
public readonly bool IsFinite()
@@ -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>
@@ -1018,7 +1050,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the vector is exactly equal
- /// to the given object (<see paramref="obj"/>).
+ /// to the given object (<paramref name="obj"/>).
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
index a2927533f8..8543052f56 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
@@ -572,7 +572,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the vector is equal
- /// to the given object (<see paramref="obj"/>).
+ /// to the given object (<paramref name="obj"/>).
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
index 1fd39632b0..10a0b14162 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
@@ -147,7 +147,7 @@ namespace Godot
/// <summary>
/// Returns a new vector with all components rounded up (towards positive infinity).
/// </summary>
- /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
+ /// <returns>A vector with <see cref="Mathf.Ceil(real_t)"/> called on each component.</returns>
public readonly Vector4 Ceil()
{
return new Vector4(Mathf.Ceil(X), Mathf.Ceil(Y), Mathf.Ceil(Z), Mathf.Ceil(W));
@@ -264,7 +264,7 @@ namespace Godot
/// <summary>
/// Returns a new vector with all components rounded down (towards negative infinity).
/// </summary>
- /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
+ /// <returns>A vector with <see cref="Mathf.Floor(real_t)"/> called on each component.</returns>
public readonly Vector4 Floor()
{
return new Vector4(Mathf.Floor(X), Mathf.Floor(Y), Mathf.Floor(Z), Mathf.Floor(W));
@@ -281,7 +281,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if this vector is finite, by calling
- /// <see cref="Mathf.IsFinite"/> on each component.
+ /// <see cref="Mathf.IsFinite(real_t)"/> on each component.
/// </summary>
/// <returns>Whether this vector is finite or not.</returns>
public readonly bool IsFinite()
@@ -832,7 +832,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the vector is exactly equal
- /// to the given object (<see paramref="obj"/>).
+ /// to the given object (<paramref name="obj"/>).
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
index bb552b939d..f813903177 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs
@@ -593,7 +593,7 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the vector is equal
- /// to the given object (<see paramref="obj"/>).
+ /// to the given object (<paramref name="obj"/>).
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 159cb91d1b..279b5cfed2 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -141,19 +141,52 @@ private:
#else // TOOLS_ENABLED
String arch = Engine::get_singleton()->get_architecture_name();
String appname_safe = path::get_csharp_project_name();
- String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
- if (!DirAccess::exists(data_dir_root)) {
- data_dir_root = exe_dir.path_join("data_Godot_" + arch);
- }
+ String packed_path = "res://.godot/mono/publish/" + arch;
+ if (DirAccess::exists(packed_path)) {
+ // The dotnet publish data is packed in the pck/zip.
+ String data_dir_root = OS::get_singleton()->get_cache_path().path_join("data_" + appname_safe + "_" + arch);
+ bool has_data = false;
+ if (!has_data) {
+ // 1. Try to access the data directly.
+ String global_packed = ProjectSettings::get_singleton()->globalize_path(packed_path);
+ if (global_packed.is_absolute_path() && FileAccess::exists(global_packed.path_join(".dotnet-publish-manifest"))) {
+ data_dir_root = global_packed;
+ has_data = true;
+ }
+ }
+ if (!has_data) {
+ // 2. Check if the data was extracted before and is up-to-date.
+ String packed_manifest = packed_path.path_join(".dotnet-publish-manifest");
+ String extracted_manifest = data_dir_root.path_join(".dotnet-publish-manifest");
+ if (FileAccess::exists(packed_manifest) && FileAccess::exists(extracted_manifest)) {
+ if (FileAccess::get_file_as_bytes(packed_manifest) == FileAccess::get_file_as_bytes(extracted_manifest)) {
+ has_data = true;
+ }
+ }
+ }
+ if (!has_data) {
+ // 3. Extract the data to a temporary location to load from there.
+ Ref<DirAccess> da = DirAccess::create_for_path(packed_path);
+ ERR_FAIL_NULL(da);
+ ERR_FAIL_COND(da->copy_dir(packed_path, data_dir_root) != OK);
+ }
+ api_assemblies_dir = data_dir_root;
+ } else {
+ // The dotnet publish data is in a directory next to the executable.
+ String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
+ if (!DirAccess::exists(data_dir_root)) {
+ data_dir_root = exe_dir.path_join("data_Godot_" + arch);
+ }
#ifdef MACOS_ENABLED
- if (!DirAccess::exists(data_dir_root)) {
- data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch);
- }
- if (!DirAccess::exists(data_dir_root)) {
- data_dir_root = res_dir.path_join("data_Godot_" + arch);
- }
+ if (!DirAccess::exists(data_dir_root)) {
+ data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch);
+ }
+ if (!DirAccess::exists(data_dir_root)) {
+ data_dir_root = res_dir.path_join("data_Godot_" + arch);
+ }
#endif
- api_assemblies_dir = data_dir_root;
+ api_assemblies_dir = data_dir_root;
+ }
#endif
}
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 4337478370..5e52f25cf4 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -55,9 +55,7 @@
// TODO mobile
#if 0
-#ifdef ANDROID_ENABLED
-#include "support/android_support.h"
-#elif defined(IOS_ENABLED)
+#ifdef IOS_ENABLED
#include "support/ios_support.h"
#endif
#endif
@@ -554,10 +552,6 @@ GDMono::~GDMono() {
finalizing_scripts_domain = false;
runtime_initialized = false;
-#if defined(ANDROID_ENABLED)
- gdmono::android::support::cleanup();
-#endif
-
singleton = nullptr;
}
diff --git a/modules/mono/mono_gd/support/android_support.cpp b/modules/mono/mono_gd/support/android_support.cpp
deleted file mode 100644
index 14b442516e..0000000000
--- a/modules/mono/mono_gd/support/android_support.cpp
+++ /dev/null
@@ -1,720 +0,0 @@
-/**************************************************************************/
-/* android_support.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 "android_support.h"
-
-#if defined(ANDROID_ENABLED)
-
-#include "../../utils/path_utils.h"
-#include "../../utils/string_utils.h"
-#include "../gd_mono_cache.h"
-#include "../gd_mono_marshal.h"
-
-#include "core/os/os.h"
-#include "core/string/ustring.h"
-
-#include "java_godot_wrapper.h"
-#include "os_android.h"
-#include "thread_jandroid.h"
-
-#include <mono/utils/mono-dl-fallback.h>
-
-#include <dlfcn.h> // dlopen, dlsym
-#include <sys/system_properties.h>
-#include <cstddef>
-
-#if __ANDROID_API__ < 24
-#include "thirdparty/misc/ifaddrs-android.h"
-#else
-#include <ifaddrs.h>
-#endif
-
-// Warning: JNI boilerplate ahead... continue at your own risk
-
-namespace gdmono {
-namespace android {
-namespace support {
-
-template <typename T>
-struct ScopedLocalRef {
- JNIEnv *env;
- T local_ref;
-
- _FORCE_INLINE_ T get() const { return local_ref; }
- _FORCE_INLINE_ operator T() const { return local_ref; }
- _FORCE_INLINE_ operator jvalue() const { return (jvalue)local_ref; }
-
- _FORCE_INLINE_ operator bool() const { return local_ref != nullptr; }
-
- _FORCE_INLINE_ bool operator==(std::nullptr_t) const {
- return local_ref == nullptr;
- }
-
- _FORCE_INLINE_ bool operator!=(std::nullptr_t) const {
- return local_ref != nullptr;
- }
-
- ScopedLocalRef(const ScopedLocalRef &) = delete;
- ScopedLocalRef &operator=(const ScopedLocalRef &) = delete;
-
- ScopedLocalRef(JNIEnv *p_env, T p_local_ref) :
- env(p_env),
- local_ref(p_local_ref) {
- }
-
- ~ScopedLocalRef() {
- if (local_ref) {
- env->DeleteLocalRef(local_ref);
- }
- }
-};
-
-bool jni_exception_check(JNIEnv *p_env) {
- if (p_env->ExceptionCheck()) {
- // Print the exception to logcat
- p_env->ExceptionDescribe();
-
- p_env->ExceptionClear();
- return true;
- }
-
- return false;
-}
-
-String app_native_lib_dir_cache;
-
-String determine_app_native_lib_dir() {
- JNIEnv *env = get_jni_env();
-
- ScopedLocalRef<jclass> activityThreadClass(env, env->FindClass("android/app/ActivityThread"));
- jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
- ScopedLocalRef<jobject> activityThread(env, env->CallStaticObjectMethod(activityThreadClass, currentActivityThread));
- jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;");
- ScopedLocalRef<jobject> ctx(env, env->CallObjectMethod(activityThread, getApplication));
-
- jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
- ScopedLocalRef<jobject> applicationInfo(env, env->CallObjectMethod(ctx, getApplicationInfo));
- jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;");
- ScopedLocalRef<jstring> nativeLibraryDir(env, (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField));
-
- String result;
-
- const char *const nativeLibraryDirUtf8 = env->GetStringUTFChars(nativeLibraryDir, nullptr);
- if (nativeLibraryDirUtf8) {
- result.parse_utf8(nativeLibraryDirUtf8);
- env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDirUtf8);
- }
-
- return result;
-}
-
-String get_app_native_lib_dir() {
- if (app_native_lib_dir_cache.is_empty()) {
- app_native_lib_dir_cache = determine_app_native_lib_dir();
- }
- return app_native_lib_dir_cache;
-}
-
-int gd_mono_convert_dl_flags(int flags) {
- // from mono's runtime-bootstrap.c
-
- int lflags = flags & MONO_DL_LOCAL ? 0 : RTLD_GLOBAL;
-
- if (flags & MONO_DL_LAZY) {
- lflags |= RTLD_LAZY;
- } else {
- lflags |= RTLD_NOW;
- }
-
- return lflags;
-}
-
-#ifndef GD_MONO_SO_NAME
-#define GD_MONO_SO_NAME "libmonosgen-2.0.so"
-#endif
-
-const char *mono_so_name = GD_MONO_SO_NAME;
-const char *godot_so_name = "libgodot_android.so";
-
-void *mono_dl_handle = nullptr;
-void *godot_dl_handle = nullptr;
-
-void *try_dlopen(const String &p_so_path, int p_flags) {
- if (!FileAccess::exists(p_so_path)) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Cannot find shared library: '%s'\n", p_so_path.utf8().get_data());
- }
- return nullptr;
- }
-
- int lflags = gd_mono_convert_dl_flags(p_flags);
-
- void *handle = dlopen(p_so_path.utf8().get_data(), lflags);
-
- if (!handle) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Failed to open shared library: '%s'. Error: '%s'\n", p_so_path.utf8().get_data(), dlerror());
- }
- return nullptr;
- }
-
- if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Successfully loaded shared library: '%s'\n", p_so_path.utf8().get_data());
- }
-
- return handle;
-}
-
-void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void *p_user_data) {
- if (p_name == nullptr) {
- // __Internal
-
- if (!mono_dl_handle) {
- String app_native_lib_dir = get_app_native_lib_dir();
- String so_path = path::join(app_native_lib_dir, mono_so_name);
-
- mono_dl_handle = try_dlopen(so_path, p_flags);
- }
-
- return mono_dl_handle;
- }
-
- String name = String::utf8(p_name);
-
- if (name.ends_with(".dll.so") || name.ends_with(".exe.so")) {
- String app_native_lib_dir = get_app_native_lib_dir();
-
- String orig_so_name = name.get_file();
- String so_name = "lib-aot-" + orig_so_name;
- String so_path = path::join(app_native_lib_dir, so_name);
-
- return try_dlopen(so_path, p_flags);
- }
-
- return nullptr;
-}
-
-void *gd_mono_android_dlsym(void *p_handle, const char *p_name, char **r_err, void *p_user_data) {
- void *sym_addr = dlsym(p_handle, p_name);
-
- if (sym_addr) {
- return sym_addr;
- }
-
- if (p_handle == mono_dl_handle && godot_dl_handle) {
- // Looking up for '__Internal' P/Invoke. We want to search in both the Mono and Godot shared libraries.
- // This is needed to resolve the monodroid P/Invoke functions that are defined at the bottom of the file.
- sym_addr = dlsym(godot_dl_handle, p_name);
-
- if (sym_addr) {
- return sym_addr;
- }
- }
-
- if (r_err) {
- *r_err = str_format_new("%s\n", dlerror());
- }
-
- return nullptr;
-}
-
-void *gd_mono_android_dlclose(void *p_handle, void *p_user_data) {
- dlclose(p_handle);
-
- // Not sure if this ever happens. Does Mono close the handle for the main module?
- if (p_handle == mono_dl_handle) {
- mono_dl_handle = nullptr;
- }
-
- return nullptr;
-}
-
-int32_t build_version_sdk_int = 0;
-
-int32_t get_build_version_sdk_int() {
- // The JNI code is the equivalent of:
- //
- // android.os.Build.VERSION.SDK_INT
-
- if (build_version_sdk_int == 0) {
- JNIEnv *env = get_jni_env();
-
- jclass versionClass = env->FindClass("android/os/Build$VERSION");
- ERR_FAIL_NULL_V(versionClass, 0);
-
- jfieldID sdkIntField = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
- ERR_FAIL_NULL_V(sdkIntField, 0);
-
- build_version_sdk_int = (int32_t)env->GetStaticIntField(versionClass, sdkIntField);
- }
-
- return build_version_sdk_int;
-}
-
-jobject certStore = nullptr; // KeyStore
-
-MonoBoolean _gd_mono_init_cert_store() {
- // The JNI code is the equivalent of:
- //
- // try {
- // certStoreLocal = KeyStore.getInstance("AndroidCAStore");
- // certStoreLocal.load(null);
- // certStore = certStoreLocal;
- // return true;
- // } catch (Exception e) {
- // return false;
- // }
-
- JNIEnv *env = get_jni_env();
-
- ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
-
- jmethodID getInstance = env->GetStaticMethodID(keyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;");
- jmethodID load = env->GetMethodID(keyStoreClass, "load", "(Ljava/security/KeyStore$LoadStoreParameter;)V");
-
- ScopedLocalRef<jstring> androidCAStoreString(env, env->NewStringUTF("AndroidCAStore"));
-
- ScopedLocalRef<jobject> certStoreLocal(env, env->CallStaticObjectMethod(keyStoreClass, getInstance, androidCAStoreString.get()));
-
- if (jni_exception_check(env)) {
- return 0;
- }
-
- env->CallVoidMethod(certStoreLocal, load, nullptr);
-
- if (jni_exception_check(env)) {
- return 0;
- }
-
- certStore = env->NewGlobalRef(certStoreLocal);
-
- return 1;
-}
-
-MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
- // The JNI code is the equivalent of:
- //
- // Certificate certificate = certStore.getCertificate(alias);
- // if (certificate == null) {
- // return null;
- // }
- // return certificate.getEncoded();
-
- MonoError mono_error;
- char *alias_utf8 = mono_string_to_utf8_checked(p_alias, &mono_error);
-
- if (!mono_error_ok(&mono_error)) {
- ERR_PRINT(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&mono_error) + "'.");
- mono_error_cleanup(&mono_error);
- return nullptr;
- }
-
- JNIEnv *env = get_jni_env();
-
- ScopedLocalRef<jstring> js_alias(env, env->NewStringUTF(alias_utf8));
- mono_free(alias_utf8);
-
- ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
- ERR_FAIL_NULL_V(keyStoreClass, nullptr);
- ScopedLocalRef<jclass> certificateClass(env, env->FindClass("java/security/cert/Certificate"));
- ERR_FAIL_NULL_V(certificateClass, nullptr);
-
- jmethodID getCertificate = env->GetMethodID(keyStoreClass, "getCertificate", "(Ljava/lang/String;)Ljava/security/cert/Certificate;");
- ERR_FAIL_NULL_V(getCertificate, nullptr);
-
- jmethodID getEncoded = env->GetMethodID(certificateClass, "getEncoded", "()[B");
- ERR_FAIL_NULL_V(getEncoded, nullptr);
-
- ScopedLocalRef<jobject> certificate(env, env->CallObjectMethod(certStore, getCertificate, js_alias.get()));
-
- if (!certificate) {
- return nullptr;
- }
-
- ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded));
- jsize encodedLength = env->GetArrayLength(encoded);
-
- MonoArray *encoded_ret = mono_array_new(mono_domain_get(), mono_get_byte_class(), encodedLength);
- uint8_t *dest = (uint8_t *)mono_array_addr(encoded_ret, uint8_t, 0);
-
- env->GetByteArrayRegion(encoded, 0, encodedLength, reinterpret_cast<jbyte *>(dest));
-
- return encoded_ret;
-}
-
-void register_internal_calls() {
- GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", _gd_mono_init_cert_store);
- GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", _gd_mono_android_cert_store_lookup);
-}
-
-void initialize() {
- // We need to set this environment variable to make the monodroid BCL use btls instead of legacy as the default provider
- OS::get_singleton()->set_environment("XA_TLS_PROVIDER", "btls");
-
- mono_dl_fallback_register(gd_mono_android_dlopen, gd_mono_android_dlsym, gd_mono_android_dlclose, nullptr);
-
- String app_native_lib_dir = get_app_native_lib_dir();
- String so_path = path::join(app_native_lib_dir, godot_so_name);
-
- godot_dl_handle = try_dlopen(so_path, gd_mono_convert_dl_flags(MONO_DL_LAZY));
-}
-
-void cleanup() {
- // This is called after shutting down the Mono runtime
-
- if (mono_dl_handle) {
- gd_mono_android_dlclose(mono_dl_handle, nullptr);
- }
-
- if (godot_dl_handle) {
- gd_mono_android_dlclose(godot_dl_handle, nullptr);
- }
-
- JNIEnv *env = get_jni_env();
-
- if (certStore) {
- env->DeleteGlobalRef(certStore);
- certStore = nullptr;
- }
-}
-} // namespace support
-} // namespace android
-} // namespace gdmono
-
-using namespace gdmono::android::support;
-
-// The following are P/Invoke functions required by the monodroid profile of the BCL.
-// These are P/Invoke functions and not internal calls, hence why they use
-// 'mono_bool' and 'const char*' instead of 'MonoBoolean' and 'MonoString*'.
-
-#define GD_PINVOKE_EXPORT extern "C" __attribute__((visibility("default")))
-
-GD_PINVOKE_EXPORT int32_t _monodroid_get_android_api_level() {
- return get_build_version_sdk_int();
-}
-
-GD_PINVOKE_EXPORT void monodroid_free(void *ptr) {
- free(ptr);
-}
-
-GD_PINVOKE_EXPORT int32_t monodroid_get_system_property(const char *p_name, char **r_value) {
- char prop_value_str[PROP_VALUE_MAX + 1] = { 0 };
-
- int len = __system_property_get(p_name, prop_value_str);
-
- if (r_value) {
- if (len >= 0) {
- *r_value = (char *)malloc(len + 1);
- ERR_FAIL_NULL_V_MSG(*r_value, -1, "Out of memory.");
- memcpy(*r_value, prop_value_str, len);
- (*r_value)[len] = '\0';
- } else {
- *r_value = nullptr;
- }
- }
-
- return len;
-}
-
-GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_up_state(const char *p_ifname, mono_bool *r_is_up) {
- // The JNI code is the equivalent of:
- //
- // NetworkInterface.getByName(p_ifname).isUp()
-
- if (!r_is_up || !p_ifname || strlen(p_ifname) == 0) {
- return 0;
- }
-
- *r_is_up = 0;
-
- JNIEnv *env = get_jni_env();
-
- jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
- ERR_FAIL_NULL_V(networkInterfaceClass, 0);
-
- jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
- ERR_FAIL_NULL_V(getByName, 0);
-
- jmethodID isUp = env->GetMethodID(networkInterfaceClass, "isUp", "()Z");
- ERR_FAIL_NULL_V(isUp, 0);
-
- ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
- ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
-
- if (!networkInterface) {
- return 0;
- }
-
- *r_is_up = (mono_bool)env->CallBooleanMethod(networkInterface, isUp);
-
- return 1;
-}
-
-GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_supports_multicast(const char *p_ifname, mono_bool *r_supports_multicast) {
- // The JNI code is the equivalent of:
- //
- // NetworkInterface.getByName(p_ifname).supportsMulticast()
-
- if (!r_supports_multicast || !p_ifname || strlen(p_ifname) == 0) {
- return 0;
- }
-
- *r_supports_multicast = 0;
-
- JNIEnv *env = get_jni_env();
-
- jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
- ERR_FAIL_NULL_V(networkInterfaceClass, 0);
-
- jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
- ERR_FAIL_NULL_V(getByName, 0);
-
- jmethodID supportsMulticast = env->GetMethodID(networkInterfaceClass, "supportsMulticast", "()Z");
- ERR_FAIL_NULL_V(supportsMulticast, 0);
-
- ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
- ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
-
- if (!networkInterface) {
- return 0;
- }
-
- *r_supports_multicast = (mono_bool)env->CallBooleanMethod(networkInterface, supportsMulticast);
-
- return 1;
-}
-
-static const int dns_servers_len = 8;
-
-static void interop_get_active_network_dns_servers(char **r_dns_servers, int *dns_servers_count) {
- // The JNI code is the equivalent of:
- //
- // ConnectivityManager connectivityManager = (ConnectivityManager)getApplicationContext()
- // .getSystemService(Context.CONNECTIVITY_SERVICE);
- // Network activeNerwork = connectivityManager.getActiveNetwork();
- // LinkProperties linkProperties = connectivityManager.getLinkProperties(activeNerwork);
- // List<String> dnsServers = linkProperties.getDnsServers().stream()
- // .map(inetAddress -> inetAddress.getHostAddress()).collect(Collectors.toList());
-
-#ifdef DEBUG_ENABLED
- CRASH_COND(get_build_version_sdk_int() < 23);
-#endif
-
- JNIEnv *env = get_jni_env();
-
- GodotJavaWrapper *godot_java = ((OS_Android *)OS::get_singleton())->get_godot_java();
- jobject activity = godot_java->get_activity();
-
- ScopedLocalRef<jclass> activityClass(env, env->GetObjectClass(activity));
- ERR_FAIL_NULL(activityClass);
-
- jmethodID getApplicationContext = env->GetMethodID(activityClass, "getApplicationContext", "()Landroid/content/Context;");
-
- ScopedLocalRef<jobject> applicationContext(env, env->CallObjectMethod(activity, getApplicationContext));
-
- ScopedLocalRef<jclass> contextClass(env, env->FindClass("android/content/Context"));
- ERR_FAIL_NULL(contextClass);
-
- jfieldID connectivityServiceField = env->GetStaticFieldID(contextClass, "CONNECTIVITY_SERVICE", "Ljava/lang/String;");
- ScopedLocalRef<jstring> connectivityServiceString(env, (jstring)env->GetStaticObjectField(contextClass, connectivityServiceField));
-
- jmethodID getSystemService = env->GetMethodID(contextClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
-
- ScopedLocalRef<jobject> connectivityManager(env, env->CallObjectMethod(applicationContext, getSystemService, connectivityServiceString.get()));
-
- if (!connectivityManager) {
- return;
- }
-
- ScopedLocalRef<jclass> connectivityManagerClass(env, env->FindClass("android/net/ConnectivityManager"));
- ERR_FAIL_NULL(connectivityManagerClass);
-
- jmethodID getActiveNetwork = env->GetMethodID(connectivityManagerClass, "getActiveNetwork", "()Landroid/net/Network;");
- ERR_FAIL_NULL(getActiveNetwork);
-
- ScopedLocalRef<jobject> activeNetwork(env, env->CallObjectMethod(connectivityManager, getActiveNetwork));
-
- if (!activeNetwork) {
- return;
- }
-
- jmethodID getLinkProperties = env->GetMethodID(connectivityManagerClass,
- "getLinkProperties", "(Landroid/net/Network;)Landroid/net/LinkProperties;");
- ERR_FAIL_NULL(getLinkProperties);
-
- ScopedLocalRef<jobject> linkProperties(env, env->CallObjectMethod(connectivityManager, getLinkProperties, activeNetwork.get()));
-
- if (!linkProperties) {
- return;
- }
-
- ScopedLocalRef<jclass> linkPropertiesClass(env, env->FindClass("android/net/LinkProperties"));
- ERR_FAIL_NULL(linkPropertiesClass);
-
- jmethodID getDnsServers = env->GetMethodID(linkPropertiesClass, "getDnsServers", "()Ljava/util/List;");
- ERR_FAIL_NULL(getDnsServers);
-
- ScopedLocalRef<jobject> dnsServers(env, env->CallObjectMethod(linkProperties, getDnsServers));
-
- if (!dnsServers) {
- return;
- }
-
- ScopedLocalRef<jclass> listClass(env, env->FindClass("java/util/List"));
- ERR_FAIL_NULL(listClass);
-
- jmethodID listSize = env->GetMethodID(listClass, "size", "()I");
- ERR_FAIL_NULL(listSize);
-
- int dnsServersCount = env->CallIntMethod(dnsServers, listSize);
-
- if (dnsServersCount > dns_servers_len) {
- dnsServersCount = dns_servers_len;
- }
-
- if (dnsServersCount <= 0) {
- return;
- }
-
- jmethodID listGet = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
- ERR_FAIL_NULL(listGet);
-
- ScopedLocalRef<jclass> inetAddressClass(env, env->FindClass("java/net/InetAddress"));
- ERR_FAIL_NULL(inetAddressClass);
-
- jmethodID getHostAddress = env->GetMethodID(inetAddressClass, "getHostAddress", "()Ljava/lang/String;");
- ERR_FAIL_NULL(getHostAddress);
-
- for (int i = 0; i < dnsServersCount; i++) {
- ScopedLocalRef<jobject> dnsServer(env, env->CallObjectMethod(dnsServers, listGet, (jint)i));
- if (!dnsServer) {
- continue;
- }
-
- ScopedLocalRef<jstring> hostAddress(env, (jstring)env->CallObjectMethod(dnsServer, getHostAddress));
- const char *host_address = env->GetStringUTFChars(hostAddress, 0);
-
- r_dns_servers[i] = strdup(host_address); // freed by the BCL
- (*dns_servers_count)++;
-
- env->ReleaseStringUTFChars(hostAddress, host_address);
- }
-
- // jesus...
-}
-
-GD_PINVOKE_EXPORT int32_t _monodroid_get_dns_servers(void **r_dns_servers_array) {
- if (!r_dns_servers_array) {
- return -1;
- }
-
- *r_dns_servers_array = nullptr;
-
- char *dns_servers[dns_servers_len];
- int dns_servers_count = 0;
-
- if (_monodroid_get_android_api_level() < 26) {
- // The 'net.dns*' system properties are no longer available in Android 8.0 (API level 26) and greater:
- // https://developer.android.com/about/versions/oreo/android-8.0-changes.html#o-pri
-
- char prop_name[] = "net.dns*";
-
- for (int i = 0; i < dns_servers_len; i++) {
- prop_name[7] = (char)(i + 0x31);
- char *prop_value;
- int32_t len = monodroid_get_system_property(prop_name, &prop_value);
-
- if (len > 0) {
- dns_servers[dns_servers_count] = strndup(prop_value, (size_t)len); // freed by the BCL
- dns_servers_count++;
- free(prop_value);
- }
- }
- } else {
- // Alternative for Oreo and greater
- interop_get_active_network_dns_servers(dns_servers, &dns_servers_count);
- }
-
- if (dns_servers_count > 0) {
- size_t ret_size = sizeof(char *) * (size_t)dns_servers_count;
- *r_dns_servers_array = malloc(ret_size); // freed by the BCL
- ERR_FAIL_NULL_V_MSG(*r_dns_servers_array, -1, "Out of memory.");
- memcpy(*r_dns_servers_array, dns_servers, ret_size);
- }
-
- return dns_servers_count;
-}
-
-GD_PINVOKE_EXPORT const char *_monodroid_timezone_get_default_id() {
- // The JNI code is the equivalent of:
- //
- // TimeZone.getDefault().getID()
-
- JNIEnv *env = get_jni_env();
-
- ScopedLocalRef<jclass> timeZoneClass(env, env->FindClass("java/util/TimeZone"));
- ERR_FAIL_NULL_V(timeZoneClass, nullptr);
-
- jmethodID getDefault = env->GetStaticMethodID(timeZoneClass, "getDefault", "()Ljava/util/TimeZone;");
- ERR_FAIL_NULL_V(getDefault, nullptr);
-
- jmethodID getID = env->GetMethodID(timeZoneClass, "getID", "()Ljava/lang/String;");
- ERR_FAIL_NULL_V(getID, nullptr);
-
- ScopedLocalRef<jobject> defaultTimeZone(env, env->CallStaticObjectMethod(timeZoneClass, getDefault));
-
- if (!defaultTimeZone) {
- return nullptr;
- }
-
- ScopedLocalRef<jstring> defaultTimeZoneID(env, (jstring)env->CallObjectMethod(defaultTimeZone, getID));
-
- if (!defaultTimeZoneID) {
- return nullptr;
- }
-
- const char *default_time_zone_id = env->GetStringUTFChars(defaultTimeZoneID, 0);
-
- char *result = strdup(default_time_zone_id); // freed by the BCL
-
- env->ReleaseStringUTFChars(defaultTimeZoneID, default_time_zone_id);
-
- return result;
-}
-
-GD_PINVOKE_EXPORT int32_t _monodroid_getifaddrs(struct ifaddrs **p_ifap) {
- return getifaddrs(p_ifap);
-}
-
-GD_PINVOKE_EXPORT void _monodroid_freeifaddrs(struct ifaddrs *p_ifap) {
- freeifaddrs(p_ifap);
-}
-
-#endif
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/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index bf34779735..c0fa6eef9e 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -340,6 +340,20 @@ RID GodotNavigationServer::region_create() {
return rid;
}
+COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled) {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND(region == nullptr);
+
+ region->set_enabled(p_enabled);
+}
+
+bool GodotNavigationServer::region_get_enabled(RID p_region) const {
+ const NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, false);
+
+ return region->get_enabled();
+}
+
COMMAND_2(region_set_use_edge_connections, RID, p_region, bool, p_enabled) {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND(region == nullptr);
@@ -446,10 +460,13 @@ COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navi
region->set_mesh(p_navigation_mesh);
}
+#ifndef DISABLE_DEPRECATED
void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) {
ERR_FAIL_COND(p_navigation_mesh.is_null());
ERR_FAIL_COND(p_root_node == nullptr);
+ WARN_PRINT_ONCE("NavigationServer3D::region_bake_navigation_mesh() is deprecated due to core threading changes. To upgrade existing code, first create a NavigationMeshSourceGeometryData3D resource. Use this resource with method parse_source_geometry_data() to parse the SceneTree for nodes that should contribute to the navigation mesh baking. The SceneTree parsing needs to happen on the main thread. After the parsing is finished use the resource with method bake_from_source_geometry_data() to bake a navigation mesh..");
+
#ifndef _3D_DISABLED
NavigationMeshGenerator::get_singleton()->clear(p_navigation_mesh);
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
@@ -458,6 +475,7 @@ void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_na
NavigationMeshGenerator::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, source_geometry_data);
#endif
}
+#endif // DISABLE_DEPRECATED
int GodotNavigationServer::region_get_connections_count(RID p_region) const {
NavRegion *region = region_owner.get_or_null(p_region);
@@ -508,6 +526,20 @@ RID GodotNavigationServer::link_get_map(const RID p_link) const {
return RID();
}
+COMMAND_2(link_set_enabled, RID, p_link, bool, p_enabled) {
+ NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND(link == nullptr);
+
+ link->set_enabled(p_enabled);
+}
+
+bool GodotNavigationServer::link_get_enabled(RID p_link) const {
+ const NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND_V(link == nullptr, false);
+
+ return link->get_enabled();
+}
+
COMMAND_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional) {
NavLink *link = link_owner.get_or_null(p_link);
ERR_FAIL_COND(link == nullptr);
@@ -784,8 +816,8 @@ COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority) {
}
RID GodotNavigationServer::obstacle_create() {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- MutexLock lock(mut_this->operations_mutex);
+ MutexLock lock(operations_mutex);
+
RID rid = obstacle_owner.make_rid();
NavObstacle *obstacle = obstacle_owner.get_or_null(rid);
obstacle->set_self(rid);
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index 6b394157bc..0b3789102c 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -135,6 +135,9 @@ public:
virtual RID region_create() override;
+ COMMAND_2(region_set_enabled, RID, p_region, bool, p_enabled);
+ virtual bool region_get_enabled(RID p_region) const override;
+
COMMAND_2(region_set_use_edge_connections, RID, p_region, bool, p_enabled);
virtual bool region_get_use_edge_connections(RID p_region) const override;
@@ -154,7 +157,9 @@ public:
virtual uint32_t region_get_navigation_layers(RID p_region) const override;
COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform);
COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navigation_mesh);
+#ifndef DISABLE_DEPRECATED
virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) override;
+#endif // DISABLE_DEPRECATED
virtual int region_get_connections_count(RID p_region) const override;
virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override;
virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override;
@@ -162,6 +167,8 @@ public:
virtual RID link_create() override;
COMMAND_2(link_set_map, RID, p_link, RID, p_map);
virtual RID link_get_map(RID p_link) const override;
+ COMMAND_2(link_set_enabled, RID, p_link, bool, p_enabled);
+ virtual bool link_get_enabled(RID p_link) const override;
COMMAND_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional);
virtual bool link_is_bidirectional(RID p_link) const override;
COMMAND_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers);
diff --git a/modules/navigation/nav_link.cpp b/modules/navigation/nav_link.cpp
index d712987a46..c693cc91c8 100644
--- a/modules/navigation/nav_link.cpp
+++ b/modules/navigation/nav_link.cpp
@@ -49,6 +49,16 @@ void NavLink::set_map(NavMap *p_map) {
}
}
+void NavLink::set_enabled(bool p_enabled) {
+ if (enabled == p_enabled) {
+ return;
+ }
+ enabled = p_enabled;
+
+ // TODO: This should not require a full rebuild as the link has not really changed.
+ link_dirty = true;
+};
+
void NavLink::set_bidirectional(bool p_bidirectional) {
if (bidirectional == p_bidirectional) {
return;
diff --git a/modules/navigation/nav_link.h b/modules/navigation/nav_link.h
index 0b8ad4db69..a7609831db 100644
--- a/modules/navigation/nav_link.h
+++ b/modules/navigation/nav_link.h
@@ -39,6 +39,7 @@ class NavLink : public NavBase {
bool bidirectional = true;
Vector3 start_position;
Vector3 end_position;
+ bool enabled = true;
bool link_dirty = true;
@@ -52,6 +53,9 @@ public:
return map;
}
+ void set_enabled(bool p_enabled);
+ bool get_enabled() const { return enabled; }
+
void set_bidirectional(bool p_bidirectional);
bool is_bidirectional() const {
return bidirectional;
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 3a1d412618..745c227fe5 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -804,6 +804,9 @@ void NavMap::sync() {
// Resize the polygon count.
int count = 0;
for (const NavRegion *region : regions) {
+ if (!region->get_enabled()) {
+ continue;
+ }
count += region->get_polygons().size();
}
polygons.resize(count);
@@ -811,6 +814,9 @@ void NavMap::sync() {
// Copy all region polygons in the map.
count = 0;
for (const NavRegion *region : regions) {
+ if (!region->get_enabled()) {
+ continue;
+ }
const LocalVector<gd::Polygon> &polygons_source = region->get_polygons();
for (uint32_t n = 0; n < polygons_source.size(); n++) {
polygons[count + n] = polygons_source[n];
diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp
index 867cf5d8fc..4e7964ed76 100644
--- a/modules/navigation/nav_region.cpp
+++ b/modules/navigation/nav_region.cpp
@@ -51,6 +51,16 @@ void NavRegion::set_map(NavMap *p_map) {
}
}
+void NavRegion::set_enabled(bool p_enabled) {
+ if (enabled == p_enabled) {
+ return;
+ }
+ enabled = p_enabled;
+
+ // TODO: This should not require a full rebuild as the region has not really changed.
+ polygons_dirty = true;
+};
+
void NavRegion::set_use_edge_connections(bool p_enabled) {
if (use_edge_connections != p_enabled) {
use_edge_connections = p_enabled;
@@ -115,11 +125,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/navigation/nav_region.h b/modules/navigation/nav_region.h
index 0c3c1b56b6..6a8ebe5336 100644
--- a/modules/navigation/nav_region.h
+++ b/modules/navigation/nav_region.h
@@ -41,6 +41,7 @@ class NavRegion : public NavBase {
Transform3D transform;
Ref<NavigationMesh> mesh;
Vector<gd::Edge::Connection> connections;
+ bool enabled = true;
bool use_edge_connections = true;
@@ -58,6 +59,9 @@ public:
polygons_dirty = true;
}
+ void set_enabled(bool p_enabled);
+ bool get_enabled() const { return enabled; }
+
void set_map(NavMap *p_map);
NavMap *get_map() const {
return map;
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/SCsub b/modules/openxr/SCsub
index 0dd41675b6..f49dc390de 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -5,22 +5,13 @@ Import("env_modules")
env_openxr = env_modules.Clone()
-#################################################
-# Add in our Khronos OpenXR loader
+# Thirdparty source files
thirdparty_obj = []
-thirdparty_dir = "#thirdparty/openxr"
-
-env_openxr.Prepend(
- CPPPATH=[
- thirdparty_dir,
- thirdparty_dir + "/include",
- thirdparty_dir + "/src",
- thirdparty_dir + "/src/common",
- thirdparty_dir + "/src/external/jsoncpp/include",
- ]
-)
+# Khronos OpenXR loader
+
+# Needs even for build against shared library, at least the defines used in public headers.
if env["platform"] == "android":
# may need to set OPENXR_ANDROID_VERSION_SUFFIX
env_openxr.AppendUnique(CPPDEFINES=["XR_OS_ANDROID", "XR_USE_PLATFORM_ANDROID"])
@@ -37,47 +28,66 @@ elif env["platform"] == "linuxbsd":
env_openxr.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
elif env["platform"] == "windows":
env_openxr.AppendUnique(CPPDEFINES=["XR_OS_WINDOWS", "NOMINMAX", "XR_USE_PLATFORM_WIN32"])
+elif env["platform"] == "macos":
+ env_openxr.AppendUnique(CPPDEFINES=["XR_OS_APPLE"])
-# may need to check and set:
-# - XR_USE_TIMESPEC
-
-env_thirdparty = env_openxr.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
-
-if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]:
- env_thirdparty["CXXFLAGS"].remove("-fno-exceptions")
-env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"])
+ # There does not seem to be a XR_USE_PLATFORM_XYZ for Apple
-# add in external jsoncpp dependency
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_reader.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_value.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_writer.cpp")
-# add in load
-if env["platform"] != "android":
- # On Android the openxr_loader is provided by separate plugins for each device
- # Build the engine using object files
- khrloader_obj = []
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c")
-
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp")
-
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp")
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_core.cpp")
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_instance.cpp")
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp")
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger.cpp")
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/manifest_file.cpp")
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp")
- env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp")
- env.modules_sources += khrloader_obj
-
-env.modules_sources += thirdparty_obj
+# may need to check and set:
+# - XR_USE_TIMESPEC
-#################################################
-# And include our module source
+if env["builtin_openxr"]:
+ thirdparty_dir = "#thirdparty/openxr"
+
+ env_openxr.Prepend(
+ CPPPATH=[
+ thirdparty_dir,
+ thirdparty_dir + "/include",
+ thirdparty_dir + "/src",
+ thirdparty_dir + "/src/common",
+ thirdparty_dir + "/src/external/jsoncpp/include",
+ ]
+ )
+
+ env_thirdparty = env_openxr.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
+
+ if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]:
+ env_thirdparty["CXXFLAGS"].remove("-fno-exceptions")
+ env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"])
+
+ # add in external jsoncpp dependency
+ thirdparty_jsoncpp_dir = thirdparty_dir + "/src/external/jsoncpp/src/lib_json/"
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_reader.cpp")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_value.cpp")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_writer.cpp")
+
+ # add in load
+ if env["platform"] != "android":
+ # On Android the openxr_loader is provided by separate plugins for each device
+ # Build the engine using object files
+ khrloader_obj = []
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c")
+
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp")
+
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_core.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_instance.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/manifest_file.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp")
+ env.modules_sources += khrloader_obj
+
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
module_obj = []
@@ -90,7 +100,7 @@ if env["platform"] == "android":
env_openxr.add_source_files(module_obj, "extensions/openxr_android_extension.cpp")
if env["vulkan"]:
env_openxr.add_source_files(module_obj, "extensions/openxr_vulkan_extension.cpp")
-if env["opengl3"]:
+if env["opengl3"] and env["platform"] != "macos":
env_openxr.add_source_files(module_obj, "extensions/openxr_opengl_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_palm_pose_extension.cpp")
@@ -104,6 +114,8 @@ env_openxr.add_source_files(module_obj, "extensions/openxr_fb_display_refresh_ra
env_openxr.add_source_files(module_obj, "extensions/openxr_pico_controller_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_wmr_controller_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_ml2_controller_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_extension_wrapper_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_api_extension.cpp")
env.modules_sources += module_obj
diff --git a/modules/openxr/config.py b/modules/openxr/config.py
index e503f12739..8ed06a1606 100644
--- a/modules/openxr/config.py
+++ b/modules/openxr/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- if platform in ("linuxbsd", "windows", "android"):
+ if platform in ("linuxbsd", "windows", "android", "macos"):
return env["openxr"] and not env["disable_3d"]
else:
# not supported on these platforms
diff --git a/modules/openxr/extensions/openxr_android_extension.cpp b/modules/openxr/extensions/openxr_android_extension.cpp
index 98687d5f20..c6082ca404 100644
--- a/modules/openxr/extensions/openxr_android_extension.cpp
+++ b/modules/openxr/extensions/openxr_android_extension.cpp
@@ -53,6 +53,7 @@ OpenXRAndroidExtension::OpenXRAndroidExtension() {
HashMap<String, bool *> OpenXRAndroidExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
+ request_extensions[XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME] = &loader_init_extension_available;
request_extensions[XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME] = &create_instance_extension_available;
return request_extensions;
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index 920bfe74b7..31f8d23268 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -36,8 +36,6 @@
#include "core/templates/hash_map.h"
#include "core/templates/rid.h"
-#include "thirdparty/openxr/src/common/xr_linear.h"
-
#include <openxr/openxr.h>
class OpenXRAPI;
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
new file mode 100644
index 0000000000..81ba9c56b8
--- /dev/null
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
@@ -0,0 +1,207 @@
+/**************************************************************************/
+/* openxr_extension_wrapper_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "openxr_extension_wrapper_extension.h"
+
+#include "../openxr_api.h"
+
+void OpenXRExtensionWrapperExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_requested_extensions);
+ GDVIRTUAL_BIND(_set_system_properties_and_get_next_pointer, "next_pointer");
+ GDVIRTUAL_BIND(_set_instance_create_info_and_get_next_pointer, "next_pointer");
+ GDVIRTUAL_BIND(_set_session_create_and_get_next_pointer, "next_pointer");
+ GDVIRTUAL_BIND(_set_swapchain_create_info_and_get_next_pointer, "next_pointer");
+ GDVIRTUAL_BIND(_on_register_metadata);
+ GDVIRTUAL_BIND(_on_before_instance_created);
+ GDVIRTUAL_BIND(_on_instance_created, "instance");
+ GDVIRTUAL_BIND(_on_instance_destroyed);
+ GDVIRTUAL_BIND(_on_session_created, "session");
+ GDVIRTUAL_BIND(_on_process);
+ GDVIRTUAL_BIND(_on_pre_render);
+ GDVIRTUAL_BIND(_on_session_destroyed);
+ GDVIRTUAL_BIND(_on_state_idle);
+ GDVIRTUAL_BIND(_on_state_ready);
+ GDVIRTUAL_BIND(_on_state_synchronized);
+ GDVIRTUAL_BIND(_on_state_visible);
+ GDVIRTUAL_BIND(_on_state_focused);
+ GDVIRTUAL_BIND(_on_state_stopping);
+ GDVIRTUAL_BIND(_on_state_loss_pending);
+ GDVIRTUAL_BIND(_on_state_exiting);
+ GDVIRTUAL_BIND(_on_event_polled, "event");
+
+ ClassDB::bind_method(D_METHOD("get_openxr_api"), &OpenXRExtensionWrapperExtension::get_openxr_api);
+ ClassDB::bind_method(D_METHOD("register_extension_wrapper"), &OpenXRExtensionWrapperExtension::register_extension_wrapper);
+}
+
+HashMap<String, bool *> OpenXRExtensionWrapperExtension::get_requested_extensions() {
+ Dictionary request_extension;
+
+ if (GDVIRTUAL_CALL(_get_requested_extensions, request_extension)) {
+ HashMap<String, bool *> result;
+ Array keys = request_extension.keys();
+ for (int i = 0; i < keys.size(); i++) {
+ String key = keys.get(i);
+ GDExtensionPtr<bool> value = VariantCaster<GDExtensionPtr<bool>>::cast(request_extension.get(key, GDExtensionPtr<bool>(nullptr)));
+ result.insert(key, value);
+ }
+ return result;
+ }
+
+ return HashMap<String, bool *>();
+}
+
+void *OpenXRExtensionWrapperExtension::set_system_properties_and_get_next_pointer(void *p_next_pointer) {
+ uint64_t pointer;
+
+ if (GDVIRTUAL_CALL(_set_system_properties_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
+ return reinterpret_cast<void *>(pointer);
+ }
+
+ return nullptr;
+}
+
+void *OpenXRExtensionWrapperExtension::set_instance_create_info_and_get_next_pointer(void *p_next_pointer) {
+ uint64_t pointer;
+
+ if (GDVIRTUAL_CALL(_set_instance_create_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
+ return reinterpret_cast<void *>(pointer);
+ }
+
+ return nullptr;
+}
+
+void *OpenXRExtensionWrapperExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
+ uint64_t pointer;
+
+ if (GDVIRTUAL_CALL(_set_session_create_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
+ return reinterpret_cast<void *>(pointer);
+ }
+
+ return nullptr;
+}
+
+void *OpenXRExtensionWrapperExtension::set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) {
+ uint64_t pointer;
+
+ if (GDVIRTUAL_CALL(_set_swapchain_create_info_and_get_next_pointer, GDExtensionPtr<void>(p_next_pointer), pointer)) {
+ return reinterpret_cast<void *>(pointer);
+ }
+
+ return nullptr;
+}
+
+void OpenXRExtensionWrapperExtension::on_register_metadata() {
+ GDVIRTUAL_CALL(_on_register_metadata);
+}
+
+void OpenXRExtensionWrapperExtension::on_before_instance_created() {
+ GDVIRTUAL_CALL(_on_before_instance_created);
+}
+
+void OpenXRExtensionWrapperExtension::on_instance_created(const XrInstance p_instance) {
+ uint64_t instance = reinterpret_cast<uint64_t>(p_instance);
+ GDVIRTUAL_CALL(_on_instance_created, instance);
+}
+
+void OpenXRExtensionWrapperExtension::on_instance_destroyed() {
+ GDVIRTUAL_CALL(_on_instance_destroyed);
+}
+
+void OpenXRExtensionWrapperExtension::on_session_created(const XrSession p_session) {
+ uint64_t session = reinterpret_cast<uint64_t>(p_session);
+ GDVIRTUAL_CALL(_on_session_created, session);
+}
+
+void OpenXRExtensionWrapperExtension::on_process() {
+ GDVIRTUAL_CALL(_on_process);
+}
+
+void OpenXRExtensionWrapperExtension::on_pre_render() {
+ GDVIRTUAL_CALL(_on_pre_render);
+}
+
+void OpenXRExtensionWrapperExtension::on_session_destroyed() {
+ GDVIRTUAL_CALL(_on_session_destroyed);
+}
+
+void OpenXRExtensionWrapperExtension::on_state_idle() {
+ GDVIRTUAL_CALL(_on_state_idle);
+}
+
+void OpenXRExtensionWrapperExtension::on_state_ready() {
+ GDVIRTUAL_CALL(_on_state_ready);
+}
+
+void OpenXRExtensionWrapperExtension::on_state_synchronized() {
+ GDVIRTUAL_CALL(_on_state_synchronized);
+}
+
+void OpenXRExtensionWrapperExtension::on_state_visible() {
+ GDVIRTUAL_CALL(_on_state_visible);
+}
+
+void OpenXRExtensionWrapperExtension::on_state_focused() {
+ GDVIRTUAL_CALL(_on_state_focused);
+}
+
+void OpenXRExtensionWrapperExtension::on_state_stopping() {
+ GDVIRTUAL_CALL(_on_state_stopping);
+}
+
+void OpenXRExtensionWrapperExtension::on_state_loss_pending() {
+ GDVIRTUAL_CALL(_on_state_loss_pending);
+}
+
+void OpenXRExtensionWrapperExtension::on_state_exiting() {
+ GDVIRTUAL_CALL(_on_state_exiting);
+}
+
+bool OpenXRExtensionWrapperExtension::on_event_polled(const XrEventDataBuffer &p_event) {
+ bool event_polled;
+
+ if (GDVIRTUAL_CALL(_on_event_polled, GDExtensionConstPtr<void>(&p_event), event_polled)) {
+ return event_polled;
+ }
+
+ return false;
+}
+
+Ref<OpenXRAPIExtension> OpenXRExtensionWrapperExtension::get_openxr_api() {
+ return openxr_api;
+}
+
+void OpenXRExtensionWrapperExtension::register_extension_wrapper() {
+ OpenXRAPI::register_extension_wrapper(this);
+}
+
+OpenXRExtensionWrapperExtension::OpenXRExtensionWrapperExtension() :
+ Object(), OpenXRExtensionWrapper() {
+ openxr_api.instantiate();
+}
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
new file mode 100644
index 0000000000..5c5e64f927
--- /dev/null
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
@@ -0,0 +1,115 @@
+/**************************************************************************/
+/* openxr_extension_wrapper_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef OPENXR_EXTENSION_WRAPPER_EXTENSION_H
+#define OPENXR_EXTENSION_WRAPPER_EXTENSION_H
+
+#include "../openxr_api_extension.h"
+#include "openxr_extension_wrapper.h"
+
+#include "core/object/ref_counted.h"
+#include "core/os/os.h"
+#include "core/os/thread_safe.h"
+#include "core/variant/native_ptr.h"
+
+class OpenXRExtensionWrapperExtension : public Object, OpenXRExtensionWrapper {
+ GDCLASS(OpenXRExtensionWrapperExtension, Object);
+
+protected:
+ _THREAD_SAFE_CLASS_
+
+ static void _bind_methods();
+
+ Ref<OpenXRAPIExtension> openxr_api;
+
+public:
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ GDVIRTUAL0R(Dictionary, _get_requested_extensions);
+
+ virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer) override;
+ virtual void *set_instance_create_info_and_get_next_pointer(void *p_next_pointer) override;
+ virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
+ virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) override;
+
+ //TODO workaround as GDExtensionPtr<void> return type results in build error in godot-cpp
+ GDVIRTUAL1R(uint64_t, _set_system_properties_and_get_next_pointer, GDExtensionPtr<void>);
+ GDVIRTUAL1R(uint64_t, _set_instance_create_info_and_get_next_pointer, GDExtensionPtr<void>);
+ GDVIRTUAL1R(uint64_t, _set_session_create_and_get_next_pointer, GDExtensionPtr<void>);
+ GDVIRTUAL1R(uint64_t, _set_swapchain_create_info_and_get_next_pointer, GDExtensionPtr<void>);
+
+ virtual void on_register_metadata() override;
+ virtual void on_before_instance_created() override;
+ virtual void on_instance_created(const XrInstance p_instance) override;
+ virtual void on_instance_destroyed() override;
+ virtual void on_session_created(const XrSession p_session) override;
+ virtual void on_process() override;
+ virtual void on_pre_render() override;
+ virtual void on_session_destroyed() override;
+
+ GDVIRTUAL0(_on_register_metadata);
+ GDVIRTUAL0(_on_before_instance_created);
+ GDVIRTUAL1(_on_instance_created, uint64_t);
+ GDVIRTUAL0(_on_instance_destroyed);
+ GDVIRTUAL1(_on_session_created, uint64_t);
+ GDVIRTUAL0(_on_process);
+ GDVIRTUAL0(_on_pre_render);
+ GDVIRTUAL0(_on_session_destroyed);
+
+ virtual void on_state_idle() override;
+ virtual void on_state_ready() override;
+ virtual void on_state_synchronized() override;
+ virtual void on_state_visible() override;
+ virtual void on_state_focused() override;
+ virtual void on_state_stopping() override;
+ virtual void on_state_loss_pending() override;
+ virtual void on_state_exiting() override;
+
+ GDVIRTUAL0(_on_state_idle);
+ GDVIRTUAL0(_on_state_ready);
+ GDVIRTUAL0(_on_state_synchronized);
+ GDVIRTUAL0(_on_state_visible);
+ GDVIRTUAL0(_on_state_focused);
+ GDVIRTUAL0(_on_state_stopping);
+ GDVIRTUAL0(_on_state_loss_pending);
+ GDVIRTUAL0(_on_state_exiting);
+
+ virtual bool on_event_polled(const XrEventDataBuffer &p_event) override;
+
+ GDVIRTUAL1R(bool, _on_event_polled, GDExtensionConstPtr<void>);
+
+ Ref<OpenXRAPIExtension> get_openxr_api();
+
+ void register_extension_wrapper();
+
+ OpenXRExtensionWrapperExtension();
+};
+
+#endif // OPENXR_EXTENSION_WRAPPER_EXTENSION_H
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/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp
index 39b5c61e8e..9038e9f458 100644
--- a/modules/openxr/extensions/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/openxr_opengl_extension.cpp
@@ -278,8 +278,8 @@ bool OpenXROpenGLExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
}
bool OpenXROpenGLExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
- XrMatrix4x4f matrix;
- XrMatrix4x4f_CreateProjectionFov(&matrix, GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
+ OpenXRUtil::XrMatrix4x4f matrix;
+ OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
diff --git a/modules/openxr/extensions/openxr_vulkan_extension.cpp b/modules/openxr/extensions/openxr_vulkan_extension.cpp
index 2902c16baf..9429d9e082 100644
--- a/modules/openxr/extensions/openxr_vulkan_extension.cpp
+++ b/modules/openxr/extensions/openxr_vulkan_extension.cpp
@@ -381,8 +381,8 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
// Even though this is a Vulkan renderer we're using OpenGL coordinate systems
- XrMatrix4x4f matrix;
- XrMatrix4x4f_CreateProjectionFov(&matrix, GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
+ OpenXRUtil::XrMatrix4x4f matrix;
+ OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 4ab280f3c3..91684b55b9 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -30,6 +30,7 @@
#include "openxr_api.h"
+#include "extensions/openxr_extension_wrapper_extension.h"
#include "openxr_interface.h"
#include "openxr_util.h"
@@ -47,7 +48,7 @@
#ifdef VULKAN_ENABLED
#define XR_USE_GRAPHICS_API_VULKAN
#endif
-#ifdef GLES3_ENABLED
+#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
#ifdef ANDROID_ENABLED
#define XR_USE_GRAPHICS_API_OPENGL_ES
#include <EGL/egl.h>
@@ -72,7 +73,7 @@
#include "extensions/openxr_vulkan_extension.h"
#endif
-#ifdef GLES3_ENABLED
+#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
#include "extensions/openxr_opengl_extension.h"
#endif
@@ -1306,7 +1307,7 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
ERR_FAIL_V(false);
#endif
} else if (p_rendering_driver == "opengl3") {
-#ifdef GLES3_ENABLED
+#if defined(GLES3_ENABLED) && !defined(MACOS_ENABLED)
graphics_extension = memnew(OpenXROpenGLExtension);
register_extension_wrapper(graphics_extension);
#else
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 96af2bfc49..9374cb7afa 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -48,8 +48,6 @@
#include "core/templates/vector.h"
#include "servers/xr/xr_pose.h"
-#include "thirdparty/openxr/src/common/xr_linear.h"
-
#include <openxr/openxr.h>
// Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initializing structs which ensures zeroing out unspecified members.
diff --git a/modules/openxr/openxr_api_extension.cpp b/modules/openxr/openxr_api_extension.cpp
new file mode 100644
index 0000000000..f0f0835f78
--- /dev/null
+++ b/modules/openxr/openxr_api_extension.cpp
@@ -0,0 +1,130 @@
+/**************************************************************************/
+/* openxr_api_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "openxr_api_extension.h"
+
+void OpenXRAPIExtension::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_instance"), &OpenXRAPIExtension::get_instance);
+ ClassDB::bind_method(D_METHOD("get_system_id"), &OpenXRAPIExtension::get_system_id);
+ ClassDB::bind_method(D_METHOD("get_session"), &OpenXRAPIExtension::get_session);
+
+ ClassDB::bind_method(D_METHOD("transform_from_pose", "pose"), &OpenXRAPIExtension::transform_from_pose);
+ ClassDB::bind_method(D_METHOD("xr_result", "result", "format", "args"), &OpenXRAPIExtension::xr_result);
+ ClassDB::bind_static_method("OpenXRAPIExtension", D_METHOD("openxr_is_enabled", "check_run_in_editor"), &OpenXRAPIExtension::openxr_is_enabled);
+ ClassDB::bind_method(D_METHOD("get_instance_proc_addr", "name"), &OpenXRAPIExtension::get_instance_proc_addr);
+ ClassDB::bind_method(D_METHOD("get_error_string", "result"), &OpenXRAPIExtension::get_error_string);
+ ClassDB::bind_method(D_METHOD("get_swapchain_format_name", "swapchain_format"), &OpenXRAPIExtension::get_swapchain_format_name);
+
+ ClassDB::bind_method(D_METHOD("is_initialized"), &OpenXRAPIExtension::is_initialized);
+ ClassDB::bind_method(D_METHOD("is_running"), &OpenXRAPIExtension::is_running);
+
+ ClassDB::bind_method(D_METHOD("get_play_space"), &OpenXRAPIExtension::get_play_space);
+ ClassDB::bind_method(D_METHOD("get_next_frame_time"), &OpenXRAPIExtension::get_next_frame_time);
+ ClassDB::bind_method(D_METHOD("can_render"), &OpenXRAPIExtension::can_render);
+}
+
+uint64_t OpenXRAPIExtension::get_instance() {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
+ return (uint64_t)OpenXRAPI::get_singleton()->get_instance();
+}
+
+uint64_t OpenXRAPIExtension::get_system_id() {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
+ return (uint64_t)OpenXRAPI::get_singleton()->get_system_id();
+}
+
+uint64_t OpenXRAPIExtension::get_session() {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
+ return (uint64_t)OpenXRAPI::get_singleton()->get_session();
+}
+
+Transform3D OpenXRAPIExtension::transform_from_pose(GDExtensionConstPtr<const void> p_pose) {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), Transform3D());
+ return OpenXRAPI::get_singleton()->transform_from_pose(*(XrPosef *)p_pose.data);
+}
+
+bool OpenXRAPIExtension::xr_result(uint64_t result, String format, Array args) {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
+ return OpenXRAPI::get_singleton()->xr_result((XrResult)result, format.utf8().get_data(), args);
+}
+
+bool OpenXRAPIExtension::openxr_is_enabled(bool p_check_run_in_editor) {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
+ return OpenXRAPI::openxr_is_enabled(p_check_run_in_editor);
+}
+
+uint64_t OpenXRAPIExtension::get_instance_proc_addr(String p_name) {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
+ CharString str = p_name.utf8();
+ PFN_xrVoidFunction addr = nullptr;
+ XrResult result = OpenXRAPI::get_singleton()->get_instance_proc_addr(str.get_data(), &addr);
+ if (result != XR_SUCCESS) {
+ return 0;
+ }
+ return reinterpret_cast<uint64_t>(addr);
+}
+
+String OpenXRAPIExtension::get_error_string(uint64_t result) {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), String());
+ return OpenXRAPI::get_singleton()->get_error_string((XrResult)result);
+}
+
+String OpenXRAPIExtension::get_swapchain_format_name(int64_t p_swapchain_format) {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), String());
+ return OpenXRAPI::get_singleton()->get_swapchain_format_name(p_swapchain_format);
+}
+
+bool OpenXRAPIExtension::is_initialized() {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
+ return OpenXRAPI::get_singleton()->is_initialized();
+}
+
+bool OpenXRAPIExtension::is_running() {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
+ return OpenXRAPI::get_singleton()->is_running();
+}
+
+uint64_t OpenXRAPIExtension::get_play_space() {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
+ return (uint64_t)OpenXRAPI::get_singleton()->get_play_space();
+}
+
+int64_t OpenXRAPIExtension::get_next_frame_time() {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
+ return (XrTime)OpenXRAPI::get_singleton()->get_next_frame_time();
+}
+
+bool OpenXRAPIExtension::can_render() {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
+ return OpenXRAPI::get_singleton()->can_render();
+}
+
+OpenXRAPIExtension::OpenXRAPIExtension() {
+}
diff --git a/modules/openxr/openxr_api_extension.h b/modules/openxr/openxr_api_extension.h
new file mode 100644
index 0000000000..98f87c7aa1
--- /dev/null
+++ b/modules/openxr/openxr_api_extension.h
@@ -0,0 +1,76 @@
+/**************************************************************************/
+/* openxr_api_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef OPENXR_API_EXTENSION_H
+#define OPENXR_API_EXTENSION_H
+
+#include "openxr_api.h"
+
+#include "core/object/ref_counted.h"
+#include "core/os/os.h"
+#include "core/os/thread_safe.h"
+#include "core/variant/native_ptr.h"
+
+class OpenXRAPIExtension : public RefCounted {
+ GDCLASS(OpenXRAPIExtension, RefCounted);
+
+protected:
+ _THREAD_SAFE_CLASS_
+
+ static void _bind_methods();
+
+public:
+ uint64_t get_instance();
+ uint64_t get_system_id();
+ uint64_t get_session();
+
+ // Helper method to convert an XrPosef to a Transform3D.
+ Transform3D transform_from_pose(GDExtensionConstPtr<const void> p_pose);
+
+ bool xr_result(uint64_t result, String format, Array args = Array());
+
+ static bool openxr_is_enabled(bool p_check_run_in_editor = true);
+
+ //TODO workaround as GDExtensionPtr<void> return type results in build error in godot-cpp
+ uint64_t get_instance_proc_addr(String p_name);
+ String get_error_string(uint64_t result);
+ String get_swapchain_format_name(int64_t p_swapchain_format);
+
+ bool is_initialized();
+ bool is_running();
+
+ uint64_t get_play_space();
+ int64_t get_next_frame_time();
+ bool can_render();
+
+ OpenXRAPIExtension();
+};
+
+#endif // OPENXR_API_EXTENSION_H
diff --git a/modules/openxr/openxr_util.cpp b/modules/openxr/openxr_util.cpp
index 0c5cdd7113..1d44233337 100644
--- a/modules/openxr/openxr_util.cpp
+++ b/modules/openxr/openxr_util.cpp
@@ -32,6 +32,8 @@
#include <openxr/openxr_reflection.h>
+#include <math.h>
+
#define XR_ENUM_CASE_STR(name, val) \
case name: \
return #name;
@@ -75,3 +77,89 @@ String OpenXRUtil::make_xr_version_string(XrVersion p_version) {
return version;
}
+
+// Copied from OpenXR xr_linear.h private header, so we can still link against
+// system-provided packages without relying on our `thirdparty` code.
+
+// Copyright (c) 2017 The Khronos Group Inc.
+// Copyright (c) 2016 Oculus VR, LLC.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+// Creates a projection matrix based on the specified dimensions.
+// The projection matrix transforms -Z=forward, +Y=up, +X=right to the appropriate clip space for the graphics API.
+// The far plane is placed at infinity if farZ <= nearZ.
+// An infinite projection matrix is preferred for rasterization because, except for
+// things *right* up against the near plane, it always provides better precision:
+// "Tightening the Precision of Perspective Rendering"
+// Paul Upchurch, Mathieu Desbrun
+// Journal of Graphics Tools, Volume 16, Issue 1, 2012
+void OpenXRUtil::XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const float tanAngleLeft,
+ const float tanAngleRight, const float tanAngleUp, float const tanAngleDown,
+ const float nearZ, const float farZ) {
+ const float tanAngleWidth = tanAngleRight - tanAngleLeft;
+
+ // Set to tanAngleDown - tanAngleUp for a clip space with positive Y down (Vulkan).
+ // Set to tanAngleUp - tanAngleDown for a clip space with positive Y up (OpenGL / D3D / Metal).
+ const float tanAngleHeight = graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown);
+
+ // Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).
+ // Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).
+ const float offsetZ = (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0;
+
+ if (farZ <= nearZ) {
+ // place the far plane at infinity
+ result->m[0] = 2.0f / tanAngleWidth;
+ result->m[4] = 0.0f;
+ result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
+ result->m[12] = 0.0f;
+
+ result->m[1] = 0.0f;
+ result->m[5] = 2.0f / tanAngleHeight;
+ result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
+ result->m[13] = 0.0f;
+
+ result->m[2] = 0.0f;
+ result->m[6] = 0.0f;
+ result->m[10] = -1.0f;
+ result->m[14] = -(nearZ + offsetZ);
+
+ result->m[3] = 0.0f;
+ result->m[7] = 0.0f;
+ result->m[11] = -1.0f;
+ result->m[15] = 0.0f;
+ } else {
+ // normal projection
+ result->m[0] = 2.0f / tanAngleWidth;
+ result->m[4] = 0.0f;
+ result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
+ result->m[12] = 0.0f;
+
+ result->m[1] = 0.0f;
+ result->m[5] = 2.0f / tanAngleHeight;
+ result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
+ result->m[13] = 0.0f;
+
+ result->m[2] = 0.0f;
+ result->m[6] = 0.0f;
+ result->m[10] = -(farZ + offsetZ) / (farZ - nearZ);
+ result->m[14] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ);
+
+ result->m[3] = 0.0f;
+ result->m[7] = 0.0f;
+ result->m[11] = -1.0f;
+ result->m[15] = 0.0f;
+ }
+}
+
+// Creates a projection matrix based on the specified FOV.
+void OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const XrFovf fov,
+ const float nearZ, const float farZ) {
+ const float tanLeft = tanf(fov.angleLeft);
+ const float tanRight = tanf(fov.angleRight);
+
+ const float tanDown = tanf(fov.angleDown);
+ const float tanUp = tanf(fov.angleUp);
+
+ XrMatrix4x4f_CreateProjection(result, graphicsApi, tanLeft, tanRight, tanUp, tanDown, nearZ, farZ);
+}
diff --git a/modules/openxr/openxr_util.h b/modules/openxr/openxr_util.h
index 8ad68c0b02..3f36ab9fca 100644
--- a/modules/openxr/openxr_util.h
+++ b/modules/openxr/openxr_util.h
@@ -44,6 +44,27 @@ public:
static String get_action_type_name(XrActionType p_action_type);
static String get_environment_blend_mode_name(XrEnvironmentBlendMode p_blend_mode);
static String make_xr_version_string(XrVersion p_version);
+
+ // Copied from OpenXR xr_linear.h private header, so we can still link against
+ // system-provided packages without relying on our `thirdparty` code.
+
+ // Column-major, pre-multiplied. This type does not exist in the OpenXR API and is provided for convenience.
+ typedef struct XrMatrix4x4f {
+ float m[16];
+ } XrMatrix4x4f;
+
+ typedef enum GraphicsAPI {
+ GRAPHICS_VULKAN,
+ GRAPHICS_OPENGL,
+ GRAPHICS_OPENGL_ES,
+ GRAPHICS_D3D
+ } GraphicsAPI;
+
+ static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const float tanAngleLeft,
+ const float tanAngleRight, const float tanAngleUp, float const tanAngleDown,
+ const float nearZ, const float farZ);
+ static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const XrFovf fov,
+ const float nearZ, const float farZ);
};
#endif // OPENXR_UTIL_H
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 27b179a788..5d636c2b70 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -36,6 +36,9 @@
#include "action_map/openxr_interaction_profile.h"
#include "action_map/openxr_interaction_profile_meta_data.h"
#include "openxr_interface.h"
+
+#include "extensions/openxr_extension_wrapper_extension.h"
+
#include "scene/openxr_hand.h"
#include "extensions/openxr_composition_layer_depth_extension.h"
@@ -87,6 +90,11 @@ static void _editor_init() {
#endif
void initialize_openxr_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_CORE) {
+ GDREGISTER_CLASS(OpenXRExtensionWrapperExtension);
+ GDREGISTER_CLASS(OpenXRAPIExtension);
+ }
+
if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
if (OpenXRAPI::openxr_is_enabled(false)) {
// Always register our extension wrappers even if we don't initialize OpenXR.
diff --git a/modules/openxr/util.h b/modules/openxr/util.h
index 6665d45007..d95bc3bb8e 100644
--- a/modules/openxr/util.h
+++ b/modules/openxr/util.h
@@ -58,6 +58,17 @@
#define EXT_TRY_INIT_XR_FUNC(name) TRY_INIT_XR_FUNC(OpenXRAPI::get_singleton(), name)
#define OPENXR_TRY_API_INIT_XR_FUNC(name) TRY_INIT_XR_FUNC(this, name)
+#define GDEXTENSION_INIT_XR_FUNC(name) \
+ do { \
+ name##_ptr = reinterpret_cast<PFN_##name>(get_openxr_api()->get_instance_proc_addr(#name)); \
+ ERR_FAIL_COND(name##_ptr == nullptr); \
+ } while (0)
+
+#define GDEXTENSION_INIT_XR_FUNC_V(name) \
+ do { \
+ name##_ptr = reinterpret_cast<PFN_##name>(get_openxr_api()->get_instance_proc_addr(#name)); \
+ ERR_FAIL_COND_V(name##_ptr == nullptr, false); \
+ } while (0)
#define EXT_PROTO_XRRESULT_FUNC1(func_name, arg1_type, arg1) \
PFN_##func_name func_name##_ptr = nullptr; \
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index ad7feeda49..7639155914 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -67,12 +67,22 @@ void ImageLoaderSVG::_replace_color_property(const HashMap<Color, Color> &p_colo
}
}
-Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample) {
+Ref<Image> ImageLoaderSVG::load_mem_svg(const uint8_t *p_svg, int p_size, float p_scale) {
+ Ref<Image> img;
+ img.instantiate();
+
+ Error err = create_image_from_utf8_buffer(img, p_svg, p_size, p_scale, false);
+ ERR_FAIL_COND_V(err, Ref<Image>());
+
+ return img;
+}
+
+Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const uint8_t *p_buffer, int p_buffer_size, float p_scale, bool p_upsample) {
ERR_FAIL_COND_V_MSG(Math::is_zero_approx(p_scale), ERR_INVALID_PARAMETER, "ImageLoaderSVG: Can't load SVG with a scale of 0.");
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
- tvg::Result result = picture->load((const char *)p_buffer.ptr(), p_buffer.size(), "svg", true);
+ tvg::Result result = picture->load((const char *)p_buffer, p_buffer_size, "svg", true);
if (result != tvg::Result::Success) {
return ERR_INVALID_DATA;
}
@@ -142,6 +152,10 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const Pa
return OK;
}
+Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample) {
+ return create_image_from_utf8_buffer(p_image, p_buffer.ptr(), p_buffer.size(), p_scale, p_upsample);
+}
+
Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map) {
if (p_color_map.size()) {
_replace_color_property(p_color_map, "stop-color=\"", p_string);
@@ -179,3 +193,7 @@ Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileacces
}
return OK;
}
+
+ImageLoaderSVG::ImageLoaderSVG() {
+ Image::_svg_scalable_mem_loader_func = load_mem_svg;
+}
diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h
index e0d2849a62..90e9458c37 100644
--- a/modules/svg/image_loader_svg.h
+++ b/modules/svg/image_loader_svg.h
@@ -36,16 +36,22 @@
class ImageLoaderSVG : public ImageFormatLoader {
static HashMap<Color, Color> forced_color_map;
- void _replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string);
+ static void _replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string);
+
+ static Ref<Image> load_mem_svg(const uint8_t *p_svg, int p_size, float p_scale);
public:
static void set_forced_color_map(const HashMap<Color, Color> &p_color_map);
- Error create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample);
- Error create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map);
+ static Error create_image_from_utf8_buffer(Ref<Image> p_image, const uint8_t *p_buffer, int p_buffer_size, float p_scale, bool p_upsample);
+ static Error create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample);
+
+ static Error create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map);
virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
+
+ ImageLoaderSVG();
};
#endif // IMAGE_LOADER_SVG_H
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index 82fbae5669..af9dae84e3 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -227,7 +227,6 @@ if env["freetype_enabled"]:
"compress.c",
"crc32.c",
"deflate.c",
- "infback.c",
"inffast.c",
"inflate.c",
"inftrees.c",
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/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 7b4c548a21..51a6ee06be 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -222,7 +222,6 @@ if env["freetype_enabled"]:
"compress.c",
"crc32.c",
"deflate.c",
- "infback.c",
"inffast.c",
"inflate.c",
"inftrees.c",
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/config.py b/modules/vorbis/config.py
index a231ef179d..9e10a58849 100644
--- a/modules/vorbis/config.py
+++ b/modules/vorbis/config.py
@@ -11,6 +11,7 @@ def get_doc_classes():
return [
"AudioStreamOggVorbis",
"AudioStreamPlaybackOggVorbis",
+ "ResourceImporterOggVorbis",
]
diff --git a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml
index e498253892..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>
@@ -14,7 +32,7 @@
<member name="bpm" type="float" setter="set_bpm" getter="get_bpm" default="0.0">
</member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false">
- If [code]true[/code], the stream will automatically loop when it reaches the end.
+ If [code]true[/code], the audio will play again from the specified [member loop_offset] once it is done playing. Useful for ambient sounds and background music.
</member>
<member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset" default="0.0">
Time in seconds at which the stream starts after being looped.
diff --git a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
new file mode 100644
index 0000000000..c2dcb832e0
--- /dev/null
+++ b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ResourceImporterOggVorbis" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Imports an Ogg Vorbis audio file for playback.
+ </brief_description>
+ <description>
+ Ogg Vorbis is a lossy audio format, with better audio quality compared to [ResourceImporterMP3] at a given bitrate.
+ In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using a MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
+ Ogg Vorbis requires more CPU to decode than [ResourceImporterWAV]. If you need to play a lot of simultaneous sounds, it's recommended to use WAV for those sounds instead, especially if targeting low-end devices.
+ </description>
+ <tutorials>
+ <link title="Importing audio samples">https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/importing_audio_samples.html</link>
+ </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">
+ The number of bars within a single beat in the audio track. This is only relevant for music that wishes to make use of interactive music functionality (not implemented yet), not sound effects.
+ A more convenient editor for [member bar_beats] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
+ </member>
+ <member name="beat_count" type="int" setter="" getter="" default="0">
+ The beat count of the audio track. This is only relevant for music that wishes to make use of interactive music functionality (not implemented yet), not sound effects.
+ A more convenient editor for [member beat_count] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
+ </member>
+ <member name="bpm" type="float" setter="" getter="" default="0">
+ The Beats Per Minute of the audio track. This should match the BPM measure that was used to compose the track. This is only relevant for music that wishes to make use of interactive music functionality (not implemented yet), not sound effects.
+ A more convenient editor for [member bpm] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
+ </member>
+ <member name="loop" type="bool" setter="" getter="" default="false">
+ If enabled, the audio will begin playing at the beginning after playback ends by reaching the end of the audio.
+ [b]Note:[/b] In [AudioStreamPlayer], the [signal AudioStreamPlayer.finished] signal won't be emitted for looping audio when it reaches the end of the audio file, as the audio will keep playing indefinitely.
+ </member>
+ <member name="loop_offset" type="float" setter="" getter="" default="0">
+ Determines where audio will start to loop after playback reaches the end of the audio. This can be used to only loop a part of the audio file, which is useful for some ambient sounds or music. The value is determined in seconds relative to the beginning of the audio. A value of [code]0.0[/code] will loop the entire audio file.
+ Only has an effect if [member loop] is [code]true[/code].
+ A more convenient editor for [member loop_offset] is provided in the [b]Advanced Import Settings[/b] dialog, as it lets you preview your changes without having to reimport the audio.
+ </member>
+ </members>
+</class>
diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp
index e131ff6dc9..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) {
@@ -44,7 +47,11 @@ void initialize_vorbis_module(ModuleInitializationLevel p_level) {
ogg_vorbis_importer.instantiate();
ResourceFormatImporter::get_singleton()->add_importer(ogg_vorbis_importer);
}
+
+ // Required to document import options in the class reference.
+ GDREGISTER_CLASS(ResourceImporterOggVorbis);
#endif
+
GDREGISTER_CLASS(AudioStreamOggVorbis);
GDREGISTER_CLASS(AudioStreamPlaybackOggVorbis);
}
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/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp
index 60cb0091e1..bc34a25733 100644
--- a/modules/webp/webp_common.cpp
+++ b/modules/webp/webp_common.cpp
@@ -84,6 +84,7 @@ Vector<uint8_t> _webp_packer(const Ref<Image> &p_image, float p_quality, bool p_
}
config.method = compression_method;
config.quality = p_quality;
+ config.use_sharp_yuv = 1;
pic.use_argb = 1;
pic.width = s.width;
pic.height = s.height;
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index 37a019eaa4..f6a0776017 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -64,7 +64,7 @@ void AndroidInputHandler::_set_key_modifier_state(Ref<InputEventWithModifiers> e
}
}
-void AndroidInputHandler::process_key_event(int p_physical_keycode, int p_unicode, int p_key_label, bool p_pressed) {
+void AndroidInputHandler::process_key_event(int p_physical_keycode, int p_unicode, int p_key_label, bool p_pressed, bool p_echo) {
static char32_t prev_wc = 0;
char32_t unicode = p_unicode;
if ((p_unicode & 0xfffffc00) == 0xd800) {
@@ -88,7 +88,7 @@ void AndroidInputHandler::process_key_event(int p_physical_keycode, int p_unicod
ev.instantiate();
Key physical_keycode = godot_code_from_android_code(p_physical_keycode);
- Key keycode = physical_keycode;
+ Key keycode;
if (unicode == '\b') { // 0x08
keycode = Key::BACKSPACE;
} else if (unicode == '\t') { // 0x09
@@ -125,6 +125,7 @@ void AndroidInputHandler::process_key_event(int p_physical_keycode, int p_unicod
ev->set_key_label(fix_key_label(p_key_label, keycode));
ev->set_unicode(fix_unicode(unicode));
ev->set_pressed(p_pressed);
+ ev->set_echo(p_echo);
_set_key_modifier_state(ev, keycode);
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index 42d1c228a8..c74c5020e3 100644
--- a/platform/android/android_input_handler.h
+++ b/platform/android/android_input_handler.h
@@ -101,7 +101,7 @@ public:
void process_magnify(Point2 p_pos, float p_factor);
void process_pan(Point2 p_pos, Vector2 p_delta);
void process_joy_event(JoypadEvent p_event);
- void process_key_event(int p_physical_keycode, int p_unicode, int p_key_label, bool p_pressed);
+ void process_key_event(int p_physical_keycode, int p_unicode, int p_key_label, bool p_pressed, bool p_echo);
};
#endif // ANDROID_INPUT_HANDLER_H
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 c94119e13d..4eb516fb63 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() {
@@ -2228,17 +2225,16 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_
}
bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
- String err;
- bool valid = false;
- const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
-
#ifdef MODULE_MONO_ENABLED
- err += TTR("Exporting to Android is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target Android with C#/Mono instead.") + "\n";
- err += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n";
// Don't check for additional errors, as this particular error cannot be resolved.
- r_error = err;
+ r_error += TTR("Exporting to Android is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target Android with C#/Mono instead.") + "\n";
+ r_error += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n";
return false;
-#endif
+#else
+
+ String err;
+ bool valid = false;
+ const bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
// Look for export templates (first official, and if defined custom templates).
@@ -2369,6 +2365,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
}
return valid;
+#endif // !MODULE_MONO_ENABLED
}
bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
@@ -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));
@@ -3299,16 +3383,17 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
Ref<Image> img = memnew(Image);
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
- ImageLoaderSVG img_loader;
- img_loader.create_image_from_string(img, _android_logo_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _android_logo_svg, EDSCALE, upsample, false);
logo = ImageTexture::create_from_image(img);
- img_loader.create_image_from_string(img, _android_run_icon_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _android_run_icon_svg, EDSCALE, upsample, false);
run_icon = ImageTexture::create_from_image(img);
#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..9c1165bf8a
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -0,0 +1,973 @@
+/**************************************************************************/
+/* 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()
+ if (mAccelerometer != null) {
+ mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (mGravity != null) {
+ mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (mMagnetometer != null) {
+ mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (mGyroscope != null) {
+ mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME)
+ }
+ 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/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index c725b1a7c9..b9ecd6971d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -147,7 +147,7 @@ public class GodotLib {
/**
* Forward regular key events.
*/
- public static native void key(int p_physical_keycode, int p_unicode, int p_key_label, boolean p_pressed);
+ public static native void key(int p_physical_keycode, int p_unicode, int p_key_label, boolean p_pressed, boolean p_echo);
/**
* Forward game device's key events.
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/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 317344f2a5..185d03fe39 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -141,7 +141,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
final int physical_keycode = event.getKeyCode();
final int unicode = event.getUnicodeChar();
final int key_label = event.getDisplayLabel();
- GodotLib.key(physical_keycode, unicode, key_label, false);
+ GodotLib.key(physical_keycode, unicode, key_label, false, event.getRepeatCount() > 0);
};
return true;
@@ -176,7 +176,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
final int physical_keycode = event.getKeyCode();
final int unicode = event.getUnicodeChar();
final int key_label = event.getDisplayLabel();
- GodotLib.key(physical_keycode, unicode, key_label, true);
+ GodotLib.key(physical_keycode, unicode, key_label, true, event.getRepeatCount() > 0);
}
return true;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
index f48dba56df..06b565c30f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
@@ -93,8 +93,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
@Override
public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) {
for (int i = 0; i < count; ++i) {
- GodotLib.key(KeyEvent.KEYCODE_DEL, 0, 0, true);
- GodotLib.key(KeyEvent.KEYCODE_DEL, 0, 0, false);
+ GodotLib.key(KeyEvent.KEYCODE_DEL, 0, 0, true, false);
+ GodotLib.key(KeyEvent.KEYCODE_DEL, 0, 0, false, false);
if (mHasSelection) {
mHasSelection = false;
@@ -115,8 +115,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
// Return keys are handled through action events
continue;
}
- GodotLib.key(0, character, 0, true);
- GodotLib.key(0, character, 0, false);
+ GodotLib.key(0, character, 0, true, false);
+ GodotLib.key(0, character, 0, false, false);
}
}
@@ -127,8 +127,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
if (characters != null) {
for (int i = 0; i < characters.length(); i++) {
final int character = characters.codePointAt(i);
- GodotLib.key(0, character, 0, true);
- GodotLib.key(0, character, 0, false);
+ GodotLib.key(0, character, 0, true, false);
+ GodotLib.key(0, character, 0, false, false);
}
}
}
@@ -136,8 +136,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
if (pActionID == EditorInfo.IME_ACTION_DONE) {
// Enter key has been pressed
mRenderView.queueOnRenderThread(() -> {
- GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, 0, true);
- GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, 0, false);
+ GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, 0, true, false);
+ GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, 0, false, false);
});
mRenderView.getView().requestFocus();
return true;
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 7c1b6023c7..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) {
@@ -385,11 +385,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(
}
// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_physical_keycode, jint p_unicode, jint p_key_label, jboolean p_pressed) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_physical_keycode, jint p_unicode, jint p_key_label, jboolean p_pressed, jboolean p_echo) {
if (step.get() <= 0) {
return;
}
- input_handler->process_key_event(p_physical_keycode, p_unicode, p_key_label, p_pressed);
+ input_handler->process_key_event(p_physical_keycode, p_unicode, p_key_label, p_pressed, p_echo);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) {
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 9158e89c13..ee6a19034c 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -49,7 +49,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JN
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jboolean p_double_tap);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_physical_keycode, jint p_unicode, jint p_key_label, jboolean p_pressed);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_physical_keycode, jint p_unicode, jint p_key_label, jboolean p_pressed, jboolean p_echo);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y);
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.cpp b/platform/ios/export/export.cpp
index e07a135861..98cc80e4a0 100644
--- a/platform/ios/export/export.cpp
+++ b/platform/ios/export/export.cpp
@@ -39,6 +39,11 @@ void register_ios_exporter_types() {
}
void register_ios_exporter() {
+#ifdef MACOS_ENABLED
+ EDITOR_DEF("export/ios/ios_deploy", "");
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/ios/ios_deploy", PROPERTY_HINT_GLOBAL_FILE, "*"));
+#endif
+
Ref<EditorExportPlatformIOS> platform;
platform.instantiate();
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index edc850e74f..544bfb71e0 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -31,11 +31,15 @@
#include "export_plugin.h"
#include "logo_svg.gen.h"
+#include "run_icon_svg.gen.h"
+#include "core/io/json.h"
#include "core/string/translation.h"
#include "editor/editor_node.h"
+#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/export/editor_export.h"
+#include "editor/plugins/script_editor_plugin.h"
#include "modules/modules_enabled.gen.h" // For mono and svg.
#ifdef MODULE_SVG_ENABLED
@@ -178,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));
@@ -1475,13 +1481,19 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
}
Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+ return _export_project_helper(p_preset, p_debug, p_path, p_flags, false, false);
+}
+
+Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_skip_ipa) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
String src_pkg_name;
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.");
@@ -1843,6 +1855,10 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
}
}
+ if (export_project_only) {
+ return OK;
+ }
+
if (ep.step("Making .xcarchive", 3)) {
return ERR_SKIP;
}
@@ -1853,11 +1869,19 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
archive_args.push_back("-scheme");
archive_args.push_back(binary_name);
archive_args.push_back("-sdk");
- archive_args.push_back("iphoneos");
+ if (p_simulator) {
+ archive_args.push_back("iphonesimulator");
+ } else {
+ archive_args.push_back("iphoneos");
+ }
archive_args.push_back("-configuration");
archive_args.push_back(p_debug ? "Debug" : "Release");
archive_args.push_back("-destination");
- archive_args.push_back("generic/platform=iOS");
+ if (p_simulator) {
+ archive_args.push_back("generic/platform=iOS Simulator");
+ } else {
+ archive_args.push_back("generic/platform=iOS");
+ }
archive_args.push_back("archive");
archive_args.push_back("-allowProvisioningUpdates");
archive_args.push_back("-archivePath");
@@ -1871,26 +1895,27 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
return FAILED;
}
- if (ep.step("Making .ipa", 4)) {
- return ERR_SKIP;
- }
- List<String> export_args;
- export_args.push_back("-exportArchive");
- export_args.push_back("-archivePath");
- export_args.push_back(archive_path);
- export_args.push_back("-exportOptionsPlist");
- export_args.push_back(dest_dir + binary_name + "/export_options.plist");
- export_args.push_back("-allowProvisioningUpdates");
- export_args.push_back("-exportPath");
- export_args.push_back(dest_dir);
- String export_str;
- err = OS::get_singleton()->execute("xcodebuild", export_args, &export_str, nullptr, true);
- ERR_FAIL_COND_V(err, err);
-
- print_line("xcodebuild (.ipa):\n" + export_str);
- if (!export_str.contains("** EXPORT SUCCEEDED **")) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR(".ipa export failed, see editor log for details."));
- return FAILED;
+ if (!p_skip_ipa) {
+ if (ep.step("Making .ipa", 4)) {
+ return ERR_SKIP;
+ }
+ List<String> export_args;
+ export_args.push_back("-exportArchive");
+ export_args.push_back("-archivePath");
+ export_args.push_back(archive_path);
+ export_args.push_back("-exportOptionsPlist");
+ export_args.push_back(dest_dir + binary_name + "/export_options.plist");
+ export_args.push_back("-allowProvisioningUpdates");
+ export_args.push_back("-exportPath");
+ export_args.push_back(dest_dir);
+ String export_str;
+ err = OS::get_singleton()->execute("xcodebuild", export_args, &export_str, nullptr, true);
+ ERR_FAIL_COND_V(err, err);
+ print_line("xcodebuild (.ipa):\n" + export_str);
+ if (!export_str.contains("** EXPORT SUCCEEDED **")) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR(".ipa export failed, see editor log for details."));
+ return FAILED;
+ }
}
#else
add_message(EXPORT_MESSAGE_WARNING, TTR("Xcode Build"), TTR(".ipa can only be built on macOS. Leaving Xcode project without building the package."));
@@ -1900,16 +1925,15 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
}
bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
- String err;
- bool valid = false;
-
#ifdef MODULE_MONO_ENABLED
- err += TTR("Exporting to iOS is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target iOS with C#/Mono instead.") + "\n";
- err += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n";
// Don't check for additional errors, as this particular error cannot be resolved.
- r_error = err;
+ r_error += TTR("Exporting to iOS is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target iOS with C#/Mono instead.") + "\n";
+ r_error += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n";
return false;
-#endif
+#else
+
+ String err;
+ bool valid = false;
// Look for export templates (first official, and if defined custom templates).
@@ -1937,6 +1961,7 @@ bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExp
}
return valid;
+#endif // !MODULE_MONO_ENABLED
}
bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
@@ -1972,26 +1997,419 @@ bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorEx
return valid;
}
+int EditorExportPlatformIOS::get_options_count() const {
+ MutexLock lock(device_lock);
+ return devices.size();
+}
+
+String EditorExportPlatformIOS::get_options_tooltip() const {
+ return TTR("Select device from the list");
+}
+
+Ref<ImageTexture> EditorExportPlatformIOS::get_option_icon(int p_index) const {
+ MutexLock lock(device_lock);
+
+ Ref<ImageTexture> icon;
+ if (p_index >= 0 || p_index < devices.size()) {
+ Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
+ if (theme.is_valid()) {
+ if (devices[p_index].simulator) {
+ icon = theme->get_icon("IOSSimulator", "EditorIcons");
+ } else if (devices[p_index].wifi) {
+ icon = theme->get_icon("IOSDeviceWireless", "EditorIcons");
+ } else {
+ icon = theme->get_icon("IOSDeviceWired", "EditorIcons");
+ }
+ }
+ }
+ return icon;
+}
+
+String EditorExportPlatformIOS::get_option_label(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, devices.size(), "");
+ MutexLock lock(device_lock);
+ return devices[p_index].name;
+}
+
+String EditorExportPlatformIOS::get_option_tooltip(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, devices.size(), "");
+ MutexLock lock(device_lock);
+ return "UUID: " + devices[p_index].id;
+}
+
+bool EditorExportPlatformIOS::is_package_name_valid(const String &p_package, String *r_error) const {
+ String pname = p_package;
+
+ if (pname.length() == 0) {
+ if (r_error) {
+ *r_error = TTR("Identifier is missing.");
+ }
+ return false;
+ }
+
+ for (int i = 0; i < pname.length(); i++) {
+ char32_t c = pname[i];
+ if (!(is_ascii_alphanumeric_char(c) || c == '-' || c == '.')) {
+ if (r_error) {
+ *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
+ }
+ return false;
+ }
+ }
+
+ return true;
+}
+
+#ifdef MACOS_ENABLED
+void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) {
+ EditorExportPlatformIOS *ea = static_cast<EditorExportPlatformIOS *>(ud);
+
+ while (!ea->quit_request.is_set()) {
+ // Nothing to do if we already know the plugins have changed.
+ if (!ea->plugins_changed.is_set()) {
+ MutexLock lock(ea->plugins_lock);
+
+ Vector<PluginConfigIOS> loaded_plugins = get_plugins();
+
+ if (ea->plugins.size() != loaded_plugins.size()) {
+ ea->plugins_changed.set();
+ } else {
+ for (int i = 0; i < ea->plugins.size(); i++) {
+ if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) {
+ ea->plugins_changed.set();
+ break;
+ }
+ }
+ }
+ }
+
+ // Check for devices updates.
+ Vector<Device> ldevices;
+
+ // Enum real devices.
+ String idepl = EDITOR_GET("export/ios/ios_deploy");
+ if (idepl.is_empty()) {
+ idepl = "ios-deploy";
+ }
+ {
+ String devices;
+ List<String> args;
+ args.push_back("-c");
+ args.push_back("-timeout");
+ args.push_back("1");
+ args.push_back("-j");
+ args.push_back("-u");
+ args.push_back("-I");
+
+ int ec = 0;
+ Error err = OS::get_singleton()->execute(idepl, args, &devices, &ec, true);
+ if (err == OK && ec == 0) {
+ Ref<JSON> json;
+ json.instantiate();
+ devices = "{ \"devices\":[" + devices.replace("}{", "},{") + "]}";
+ err = json->parse(devices);
+ if (err == OK) {
+ Dictionary data = json->get_data();
+ Array devices = data["devices"];
+ for (int i = 0; i < devices.size(); i++) {
+ Dictionary device_event = devices[i];
+ if (device_event["Event"] == "DeviceDetected") {
+ Dictionary device_info = device_event["Device"];
+ Device nd;
+ nd.id = device_info["DeviceIdentifier"];
+ nd.name = device_info["DeviceName"].operator String() + " (connected through " + device_event["Interface"].operator String() + ")";
+ nd.wifi = device_event["Interface"] == "WIFI";
+ nd.simulator = false;
+ ldevices.push_back(nd);
+ }
+ }
+ }
+ }
+ }
+
+ // Enum simulators
+ if (FileAccess::exists("/usr/bin/xcrun") || FileAccess::exists("/bin/xcrun")) {
+ String devices;
+ List<String> args;
+ args.push_back("simctl");
+ args.push_back("list");
+ args.push_back("devices");
+ args.push_back("-j");
+
+ int ec = 0;
+ Error err = OS::get_singleton()->execute("xcrun", args, &devices, &ec, true);
+ if (err == OK && ec == 0) {
+ Ref<JSON> json;
+ json.instantiate();
+ err = json->parse(devices);
+ if (err == OK) {
+ Dictionary data = json->get_data();
+ Dictionary devices = data["devices"];
+ for (const Variant *key = devices.next(nullptr); key; key = devices.next(key)) {
+ Array os_devices = devices[*key];
+ for (int i = 0; i < os_devices.size(); i++) {
+ Dictionary device_info = os_devices[i];
+ if (device_info["isAvailable"].operator bool() && device_info["state"] == "Booted") {
+ Device nd;
+ nd.id = device_info["udid"];
+ nd.name = device_info["name"].operator String() + " (simulator)";
+ nd.simulator = true;
+ ldevices.push_back(nd);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Update device list.
+ {
+ MutexLock lock(ea->device_lock);
+
+ bool different = false;
+
+ if (ea->devices.size() != ldevices.size()) {
+ different = true;
+ } else {
+ for (int i = 0; i < ea->devices.size(); i++) {
+ if (ea->devices[i].id != ldevices[i].id) {
+ different = true;
+ break;
+ }
+ }
+ }
+
+ if (different) {
+ ea->devices = ldevices;
+ ea->devices_changed.set();
+ }
+ }
+
+ uint64_t sleep = 200;
+ uint64_t wait = 3000000;
+ uint64_t time = OS::get_singleton()->get_ticks_usec();
+ while (OS::get_singleton()->get_ticks_usec() - time < wait) {
+ OS::get_singleton()->delay_usec(1000 * sleep);
+ if (ea->quit_request.is_set()) {
+ break;
+ }
+ }
+ }
+}
+#endif
+
+Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+#ifdef MACOS_ENABLED
+ ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER);
+
+ String can_export_error;
+ bool can_export_missing_templates;
+ if (!can_export(p_preset, can_export_error, can_export_missing_templates)) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), can_export_error);
+ return ERR_UNCONFIGURED;
+ }
+
+ MutexLock lock(device_lock);
+
+ EditorProgress ep("run", vformat(TTR("Running on %s"), devices[p_device].name), 3);
+
+ String id = "tmpexport." + uitos(OS::get_singleton()->get_unix_time());
+
+ Ref<DirAccess> filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V_MSG(filesystem_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + EditorPaths::get_singleton()->get_cache_dir() + "'.");
+ filesystem_da->make_dir_recursive(EditorPaths::get_singleton()->get_cache_dir().path_join(id));
+ String tmp_export_path = EditorPaths::get_singleton()->get_cache_dir().path_join(id).path_join("export.ipa");
+
+#define CLEANUP_AND_RETURN(m_err) \
+ { \
+ if (filesystem_da->change_dir(EditorPaths::get_singleton()->get_cache_dir().path_join(id)) == OK) { \
+ filesystem_da->erase_contents_recursive(); \
+ filesystem_da->change_dir(".."); \
+ filesystem_da->remove(id); \
+ } \
+ return m_err; \
+ } \
+ ((void)0)
+
+ Device dev = devices[p_device];
+
+ // Export before sending to device.
+ Error err = _export_project_helper(p_preset, true, tmp_export_path, p_debug_flags, dev.simulator, true);
+
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ Vector<String> cmd_args_list;
+ String host = EDITOR_GET("network/debug/remote_host");
+ int remote_port = (int)EDITOR_GET("network/debug/remote_port");
+
+ if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST) {
+ host = "localhost";
+ }
+
+ if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) {
+ int port = EDITOR_GET("filesystem/file_server/port");
+ String passwd = EDITOR_GET("filesystem/file_server/password");
+ cmd_args_list.push_back("--remote-fs");
+ cmd_args_list.push_back(host + ":" + itos(port));
+ if (!passwd.is_empty()) {
+ cmd_args_list.push_back("--remote-fs-password");
+ cmd_args_list.push_back(passwd);
+ }
+ }
+
+ if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
+ cmd_args_list.push_back("--remote-debug");
+
+ cmd_args_list.push_back(get_debug_protocol() + host + ":" + String::num(remote_port));
+
+ List<String> breakpoints;
+ ScriptEditor::get_singleton()->get_breakpoints(&breakpoints);
+
+ if (breakpoints.size()) {
+ cmd_args_list.push_back("--breakpoints");
+ String bpoints;
+ for (const List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
+ bpoints += E->get().replace(" ", "%20");
+ if (E->next()) {
+ bpoints += ",";
+ }
+ }
+
+ cmd_args_list.push_back(bpoints);
+ }
+ }
+
+ if (p_debug_flags & DEBUG_FLAG_VIEW_COLLISIONS) {
+ cmd_args_list.push_back("--debug-collisions");
+ }
+
+ if (p_debug_flags & DEBUG_FLAG_VIEW_NAVIGATION) {
+ cmd_args_list.push_back("--debug-navigation");
+ }
+
+ if (dev.simulator) {
+ // Deploy and run on simulator.
+ if (ep.step("Installing to simulator...", 3)) {
+ CLEANUP_AND_RETURN(ERR_SKIP);
+ } else {
+ List<String> args;
+ args.push_back("simctl");
+ args.push_back("install");
+ args.push_back(dev.id);
+ args.push_back(EditorPaths::get_singleton()->get_cache_dir().path_join(id).path_join("export.xcarchive/Products/Applications/export.app"));
+
+ String log;
+ int ec;
+ err = OS::get_singleton()->execute("xcrun", args, &log, &ec, true);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Run"), TTR("Could not start simctl executable."));
+ CLEANUP_AND_RETURN(err);
+ }
+ if (ec != 0) {
+ print_line("simctl install:\n" + log);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), TTR("Installation failed, see editor log for details."));
+ CLEANUP_AND_RETURN(ERR_UNCONFIGURED);
+ }
+ }
+
+ if (ep.step("Running on simulator...", 4)) {
+ CLEANUP_AND_RETURN(ERR_SKIP);
+ } else {
+ List<String> args;
+ args.push_back("simctl");
+ args.push_back("launch");
+ args.push_back(dev.id);
+ args.push_back(p_preset->get("application/bundle_identifier"));
+ for (const String &E : cmd_args_list) {
+ args.push_back(E);
+ }
+
+ String log;
+ int ec;
+ err = OS::get_singleton()->execute("xcrun", args, &log, &ec, true);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Run"), TTR("Could not start simctl executable."));
+ CLEANUP_AND_RETURN(err);
+ }
+ if (ec != 0) {
+ print_line("simctl launch:\n" + log);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), TTR("Running failed, see editor log for details."));
+ }
+ }
+ } else {
+ // Deploy and run on real device.
+ if (ep.step("Installing and running on device...", 4)) {
+ CLEANUP_AND_RETURN(ERR_SKIP);
+ } else {
+ List<String> args;
+ args.push_back("-u");
+ args.push_back("-I");
+ args.push_back("--id");
+ args.push_back(dev.id);
+ args.push_back("--justlaunch");
+ args.push_back("--bundle");
+ args.push_back(EditorPaths::get_singleton()->get_cache_dir().path_join(id).path_join("export.xcarchive/Products/Applications/export.app"));
+ String app_args;
+ for (const String &E : cmd_args_list) {
+ app_args += E + " ";
+ }
+ if (!app_args.is_empty()) {
+ args.push_back("--args");
+ args.push_back(app_args);
+ }
+
+ String idepl = EDITOR_GET("export/ios/ios_deploy");
+ if (idepl.is_empty()) {
+ idepl = "ios-deploy";
+ }
+ String log;
+ int ec;
+ err = OS::get_singleton()->execute(idepl, args, &log, &ec, true);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Run"), TTR("Could not start ios-deploy executable."));
+ CLEANUP_AND_RETURN(err);
+ }
+ if (ec != 0) {
+ print_line("ios-deploy:\n" + log);
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), TTR("Installation/running failed, see editor log for details."));
+ CLEANUP_AND_RETURN(ERR_UNCONFIGURED);
+ }
+ }
+ }
+
+ CLEANUP_AND_RETURN(OK);
+
+#undef CLEANUP_AND_RETURN
+#else
+ return ERR_UNCONFIGURED;
+#endif
+}
+
EditorExportPlatformIOS::EditorExportPlatformIOS() {
if (EditorNode::get_singleton()) {
#ifdef MODULE_SVG_ENABLED
Ref<Image> img = memnew(Image);
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
- ImageLoaderSVG img_loader;
- img_loader.create_image_from_string(img, _ios_logo_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _ios_logo_svg, EDSCALE, upsample, false);
logo = ImageTexture::create_from_image(img);
+
+ ImageLoaderSVG::create_image_from_string(img, _ios_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
#endif
plugins_changed.set();
-#ifndef ANDROID_ENABLED
+ devices_changed.set();
+#ifdef MACOS_ENABLED
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
#endif
}
}
EditorExportPlatformIOS::~EditorExportPlatformIOS() {
-#ifndef ANDROID_ENABLED
+#ifdef MACOS_ENABLED
quit_request.set();
if (check_for_changes_thread.is_started()) {
check_for_changes_thread.wait_to_finish();
diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h
index 6616bbd714..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>
@@ -58,15 +59,30 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
GDCLASS(EditorExportPlatformIOS, EditorExportPlatform);
Ref<ImageTexture> logo;
+ Ref<ImageTexture> run_icon;
// Plugins
mutable SafeFlag plugins_changed;
-#ifndef ANDROID_ENABLED
+ SafeFlag devices_changed;
+
+ struct Device {
+ String id;
+ String name;
+ bool simulator = false;
+ bool wifi = false;
+ };
+
+ Vector<Device> devices;
+ Mutex device_lock;
+
+ Mutex plugins_lock;
+ mutable Vector<PluginConfigIOS> plugins;
+#ifdef MACOS_ENABLED
Thread check_for_changes_thread;
SafeFlag quit_request;
+
+ static void _check_for_changes_poll_thread(void *ud);
#endif
- Mutex plugins_lock;
- mutable Vector<PluginConfigIOS> plugins;
typedef Error (*FileHandler)(String p_file, void *p_userdata);
static Error _walk_dir_recursive(Ref<DirAccess> &p_da, FileHandler p_handler, void *p_userdata);
@@ -122,64 +138,9 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug);
- bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
- String pname = p_package;
-
- if (pname.length() == 0) {
- if (r_error) {
- *r_error = TTR("Identifier is missing.");
- }
- return false;
- }
-
- for (int i = 0; i < pname.length(); i++) {
- char32_t c = pname[i];
- if (!(is_ascii_alphanumeric_char(c) || c == '-' || c == '.')) {
- if (r_error) {
- *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
- }
- return false;
- }
- }
-
- return true;
- }
+ Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_skip_ipa);
-#ifndef ANDROID_ENABLED
- static void _check_for_changes_poll_thread(void *ud) {
- EditorExportPlatformIOS *ea = static_cast<EditorExportPlatformIOS *>(ud);
-
- while (!ea->quit_request.is_set()) {
- // Nothing to do if we already know the plugins have changed.
- if (!ea->plugins_changed.is_set()) {
- MutexLock lock(ea->plugins_lock);
-
- Vector<PluginConfigIOS> loaded_plugins = get_plugins();
-
- if (ea->plugins.size() != loaded_plugins.size()) {
- ea->plugins_changed.set();
- } else {
- for (int i = 0; i < ea->plugins.size(); i++) {
- if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) {
- ea->plugins_changed.set();
- break;
- }
- }
- }
- }
-
- uint64_t wait = 3000000;
- uint64_t time = OS::get_singleton()->get_ticks_usec();
- while (OS::get_singleton()->get_ticks_usec() - time < wait) {
- OS::get_singleton()->delay_usec(300000);
-
- if (ea->quit_request.is_set()) {
- break;
- }
- }
- }
- }
-#endif
+ bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;
protected:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
@@ -191,6 +152,23 @@ public:
virtual String get_name() const override { return "iOS"; }
virtual String get_os_name() const override { return "iOS"; }
virtual Ref<Texture2D> get_logo() const override { return logo; }
+ virtual Ref<Texture2D> get_run_icon() const override { return run_icon; }
+
+ virtual int get_options_count() const override;
+ virtual String get_options_tooltip() const override;
+ virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
+ virtual String get_option_label(int p_index) const override;
+ virtual String get_option_tooltip(int p_index) const override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+
+ virtual bool poll_export() override {
+ bool dc = devices_changed.is_set();
+ if (dc) {
+ // don't clear unless we're reporting true, to avoid race
+ devices_changed.clear();
+ }
+ return dc;
+ }
virtual bool should_update_export_options() override {
bool export_options_changed = plugins_changed.is_set();
diff --git a/platform/ios/export/run_icon.svg b/platform/ios/export/run_icon.svg
new file mode 100644
index 0000000000..859c58409e
--- /dev/null
+++ b/platform/ios/export/run_icon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#bfbfbf" d="M.462 11.653H1.72V6.296H.462Zm.627-6.059a.687.687 0 0 0 .702-.682.688.688 0 0 0-.702-.687.687.687 0 0 0-.698.687c0 .38.309.682.698.682zM5.91 4.24c-2.127 0-3.461 1.45-3.461 3.77 0 2.32 1.333 3.765 3.461 3.765 2.123 0 3.457-1.445 3.457-3.765 0-2.32-1.334-3.77-3.457-3.77zm0 1.112c1.299 0 2.128 1.03 2.128 2.658 0 1.622-.829 2.653-2.128 2.653-1.304 0-2.127-1.03-2.127-2.653 0-1.627.823-2.658 2.127-2.658zm3.988 4.25c.055 1.344 1.157 2.173 2.835 2.173 1.764 0 2.876-.87 2.876-2.254 0-1.086-.627-1.698-2.108-2.037l-.839-.192c-.895-.212-1.263-.495-1.263-.98 0-.607.556-1.01 1.38-1.01.834 0 1.405.408 1.465 1.09h1.244c-.03-1.283-1.092-2.152-2.699-2.152-1.587 0-2.714.874-2.714 2.168 0 1.041.637 1.688 1.981 1.997l.945.222c.92.217 1.294.52 1.294 1.046 0 .606-.611 1.041-1.49 1.041-.89 0-1.562-.44-1.643-1.112H9.899Z" style="stroke-width:.502532;fill:#fff"/></svg>
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index ce743fbf8a..a723bb5d58 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -291,6 +291,9 @@ def configure(env: "Environment"):
# No pkgconfig file so far, hardcode expected lib name.
env.Append(LIBS=["embree3"])
+ if not env["builtin_openxr"]:
+ env.ParseConfig("pkg-config openxr --cflags --libs")
+
if env["fontconfig"]:
if not env["use_sowrap"]:
if os.system("pkg-config --exists fontconfig") == 0: # 0 means found
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index f74bdf3516..40151b1a02 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -521,11 +521,10 @@ EditorExportPlatformLinuxBSD::EditorExportPlatformLinuxBSD() {
Ref<Image> img = memnew(Image);
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
- ImageLoaderSVG img_loader;
- img_loader.create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false);
set_logo(ImageTexture::create_from_image(img));
- img_loader.create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false);
run_icon = ImageTexture::create_from_image(img);
#endif
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/export/run_icon.svg b/platform/linuxbsd/export/run_icon.svg
index 56465a0df3..ad58bcd5c7 100644
--- a/platform/linuxbsd/export/run_icon.svg
+++ b/platform/linuxbsd/export/run_icon.svg
@@ -1 +1 @@
-<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M7.941 13.966a3.62 3.62 0 0 1-.096.444 2.129 2.129 0 0 1-.31.668c.15.01.305.01.464.003.16.008.314.007.465-.003a2.129 2.129 0 0 1-.31-.668 3.62 3.62 0 0 1-.097-.444l-.058.001-.058-.001z" fill="#333" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/><path d="M10.688 10.793c-.297.005-.441.299-.707.33-.328.038-.533-.34-.996-.058-.463.283-.862 3.528.212 3.97 1.074.442 3.072-2.146 2.942-2.673-.13-.527-.542-.403-.747-.66-.206-.258 0-.666-.47-.86a.588.588 0 0 0-.234-.05zm-5.411 0a.62.62 0 0 0-.199.05c-.47.193-.264.601-.47.859-.205.257-.618.133-.748.66s1.867 3.115 2.942 2.673c1.074-.442.674-3.687.211-3.97-.463-.283-.668.096-.995.058-.277-.032-.42-.349-.741-.33z" fill="#f4bb37" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/><path d="M8 .914c-1.386 0-2.2.845-2.353 1.985-.153 1.14.094 1.348-.29 2.515s-2.103 3.168-2.063 5.013c.012.575.078 1.072.194 1.507a1.25 1.25 0 0 1 .503-.47 4.37 4.37 0 0 1 .204-.09c.004-.03.019-.098.046-.23.038-.182.183-.467.43-.654a4.773 4.773 0 0 1-.006-.172c-.029-1.431 1.45-2.982 1.723-3.888.272-.905.154-.998.199-1.223.045-.225.218-.487.468-.696a.11.11 0 0 1 .07-.028c.228-.003.456.826.897.827.44 0 .67-1.01.923-.799.25.21.423.471.468.696.045.225-.073.318.199 1.223.272.906 1.75 2.457 1.722 3.888-.001.058-.004.114-.007.17a1.2 1.2 0 0 1 .432.656c.027.132.042.2.046.23.027.01.085.037.204.092.153.07.36.236.502.47.115-.435.183-.933.195-1.509.04-1.845-1.681-3.846-2.065-5.013-.383-1.167-.135-1.376-.288-2.515C10.2 1.759 9.385.914 7.999.914Z" fill="#333" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/></svg>
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M7.941 13.966a3.62 3.62 0 0 1-.096.444 2.129 2.129 0 0 1-.31.668c.15.01.305.01.464.003.16.008.314.007.465-.003a2.129 2.129 0 0 1-.31-.668 3.62 3.62 0 0 1-.097-.444zM8 .914c-1.386 0-2.2.845-2.353 1.985-.153 1.14.094 1.348-.29 2.515s-2.103 3.168-2.063 5.013c.012.575.078 1.072.194 1.507a1.25 1.25 0 0 1 .503-.47 4.37 4.37 0 0 1 .204-.09c.004-.03.019-.098.046-.23.038-.182.183-.467.43-.654a4.773 4.773 0 0 1-.006-.172c-.029-1.431 1.45-2.982 1.723-3.888.272-.905.154-.998.199-1.223.045-.225.218-.487.468-.696.253-.211.483.798.945.799.462 0 .692-1.01.945-.799.25.21.423.471.468.696.045.225-.073.318.199 1.223.272.906 1.75 2.457 1.722 3.888a4.773 4.773 0 0 0-.007.17 1.2 1.2 0 0 1 .432.656c.027.132.042.2.046.23.027.01.085.037.204.092.153.07.36.236.502.47.115-.435.183-.933.195-1.509.04-1.845-1.681-3.846-2.065-5.013-.383-1.167-.135-1.376-.288-2.515C10.2 1.759 9.385.914 7.999.914z" fill="#333" style="fill:#e0e0e0;fill-opacity:1"/><path d="M10.688 10.793c-.297.005-.441.299-.707.33-.328.038-.533-.34-.996-.058-.463.283-.862 3.528.212 3.97 1.074.442 3.072-2.146 2.942-2.673-.13-.527-.542-.403-.747-.66-.206-.258 0-.666-.47-.86a.588.588 0 0 0-.234-.05zm-5.411 0a.62.62 0 0 0-.199.05c-.47.193-.264.601-.47.859-.205.257-.618.133-.748.66s1.867 3.115 2.942 2.673c1.074-.442.674-3.687.211-3.97-.463-.283-.668.096-.995.058-.277-.032-.42-.349-.741-.33z" fill="#f4bb37" style="fill:#e0e0e0;fill-opacity:1"/></svg>
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index ab79885fb4..342cff82e9 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -98,19 +98,20 @@ static bool detect_sandbox() {
JoypadLinux::JoypadLinux(Input *in) {
#ifdef UDEV_ENABLED
-#ifdef SOWRAP_ENABLED
-#ifdef DEBUG_ENABLED
- int dylibloader_verbose = 1;
-#else
- int dylibloader_verbose = 0;
-#endif
if (detect_sandbox()) {
// Linux binaries in sandboxes / containers need special handling because
// libudev doesn't work there. So we need to fallback to manual parsing
// of /dev/input in such case.
use_udev = false;
print_verbose("JoypadLinux: udev enabled, but detected incompatible sandboxed mode. Falling back to /dev/input to detect joypads.");
- } else {
+ }
+#ifdef SOWRAP_ENABLED
+ else {
+#ifdef DEBUG_ENABLED
+ int dylibloader_verbose = 1;
+#else
+ int dylibloader_verbose = 0;
+#endif
use_udev = initialize_libudev(dylibloader_verbose) == 0;
if (use_udev) {
if (!udev_new || !udev_unref || !udev_enumerate_new || !udev_enumerate_add_match_subsystem || !udev_enumerate_scan_devices || !udev_enumerate_get_list_entry || !udev_list_entry_get_next || !udev_list_entry_get_name || !udev_device_new_from_syspath || !udev_device_get_devnode || !udev_device_get_action || !udev_device_unref || !udev_enumerate_unref || !udev_monitor_new_from_netlink || !udev_monitor_filter_add_match_subsystem_devtype || !udev_monitor_enable_receiving || !udev_monitor_get_fd || !udev_monitor_receive_device || !udev_monitor_unref) {
@@ -124,10 +125,11 @@ JoypadLinux::JoypadLinux(Input *in) {
print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
}
}
-#endif
+#endif // SOWRAP_ENABLED
#else
print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads.");
-#endif
+#endif // UDEV_ENABLED
+
input = in;
monitor_joypads_thread.start(monitor_joypads_thread_func, this);
joypad_events_thread.start(joypad_events_thread_func, this);
@@ -391,6 +393,16 @@ void JoypadLinux::open_joypad(const char *p_path) {
return;
}
+ uint16_t vendor = BSWAP16(inpid.vendor);
+ uint16_t product = BSWAP16(inpid.product);
+ uint16_t version = BSWAP16(inpid.version);
+
+ if (input->should_ignore_device(vendor, product)) {
+ // This can be true in cases where Steam is passing information into the game to ignore
+ // original gamepads when using virtual rebindings (See SteamInput).
+ return;
+ }
+
MutexLock lock(joypads_mutex[joy_num]);
Joypad &joypad = joypads[joy_num];
joypad.reset();
@@ -399,10 +411,6 @@ void JoypadLinux::open_joypad(const char *p_path) {
setup_joypad_properties(joypad);
sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
if (inpid.vendor && inpid.product && inpid.version) {
- uint16_t vendor = BSWAP16(inpid.vendor);
- uint16_t product = BSWAP16(inpid.product);
- uint16_t version = BSWAP16(inpid.version);
-
sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor, 0, product, 0, version, 0);
input->joy_connection_changed(joy_num, true, name, uid);
} else {
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 310778388b..14d02a73c8 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -954,45 +954,33 @@ static String get_mountpoint(const String &p_path) {
}
Error OS_LinuxBSD::move_to_trash(const String &p_path) {
- String path = p_path.rstrip("/"); // Strip trailing slash when path points to a directory
+ // We try multiple methods, until we find one that works.
+ // So we only return on success until we exhausted possibilities.
+ String path = p_path.rstrip("/"); // Strip trailing slash when path points to a directory.
int err_code;
List<String> args;
args.push_back(path);
- args.push_front("trash"); // The command is `gio trash <file_name>` so we need to add it to args.
+
+ args.push_front("trash"); // The command is `gio trash <file_name>` so we add it before the path.
Error result = execute("gio", args, nullptr, &err_code); // For GNOME based machines.
- if (result == OK) { // The `execute` function has done its job without errors.
- if (!err_code) { // The shell command has been executed without errors.
- return OK;
- } else if (err_code == 1) {
- ERR_PRINT("move_to_trash: No such file or directory as " + path + ".");
- return ERR_FILE_NOT_FOUND;
- }
+ if (result == OK && err_code == 0) { // Success.
+ return OK;
}
args.pop_front();
args.push_front("move");
args.push_back("trash:/"); // The command is `kioclient5 move <file_name> trash:/`.
result = execute("kioclient5", args, nullptr, &err_code); // For KDE based machines.
- if (result == OK) { // The `execute` function has done its job without errors.
- if (!err_code) { // The shell command has been executed without errors.
- return OK;
- } else if (err_code == 1) {
- ERR_PRINT("move_to_trash: No such file or directory as " + path + ".");
- return ERR_FILE_NOT_FOUND;
- }
+ if (result == OK && err_code == 0) {
+ return OK;
}
args.pop_front();
args.pop_back();
result = execute("gvfs-trash", args, nullptr, &err_code); // For older Linux machines.
- if (result == OK) { // The `execute` function has done its job without errors.
- if (!err_code) { // The shell command has been executed without errors.
- return OK;
- } else if (err_code == 1) {
- ERR_PRINT("move_to_trash: No such file or directory as " + path + ".");
- return ERR_FILE_NOT_FOUND;
- }
+ if (result == OK && err_code == 0) {
+ return OK;
}
// If the commands `kioclient5`, `gio` or `gvfs-trash` don't work on the system we do it manually.
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 8724bc871a..2643cd3b1a 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"
@@ -1449,6 +1449,12 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
DEBUG_LOG_X11("delete_sub_window: %lu (%u) \n", wd.x11_window, p_id);
+ window_set_rect_changed_callback(Callable(), p_id);
+ window_set_window_event_callback(Callable(), p_id);
+ window_set_input_event_callback(Callable(), p_id);
+ window_set_input_text_callback(Callable(), p_id);
+ window_set_drop_files_callback(Callable(), p_id);
+
while (wd.transient_children.size()) {
window_set_transient(*wd.transient_children.begin(), INVALID_WINDOW_ID);
}
@@ -2980,6 +2986,30 @@ Key DisplayServerX11::keyboard_get_keycode_from_physical(Key p_keycode) const {
return (Key)(key | modifiers);
}
+Key DisplayServerX11::keyboard_get_label_from_physical(Key p_keycode) const {
+ Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;
+ Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK;
+ unsigned int xkeycode = KeyMappingX11::get_xlibcode(keycode_no_mod);
+ KeySym xkeysym = XkbKeycodeToKeysym(x11_display, xkeycode, keyboard_get_current_layout(), 0);
+ if (is_ascii_lower_case(xkeysym)) {
+ xkeysym -= ('a' - 'A');
+ }
+
+ Key key = KeyMappingX11::get_keycode(xkeysym);
+#ifdef XKB_ENABLED
+ if (xkb_loaded_v08p) {
+ String keysym = String::chr(xkb_keysym_to_utf32(xkb_keysym_to_upper(xkeysym)));
+ key = fix_key_label(keysym[0], KeyMappingX11::get_keycode(xkeysym));
+ }
+#endif
+
+ // If not found, fallback to QWERTY.
+ // This should match the behavior of the event pump
+ if (key == Key::NONE) {
+ return p_keycode;
+ }
+ return (Key)(key | modifiers);
+}
DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) {
Atom actual_type = None;
int actual_format = 0;
@@ -4880,6 +4910,8 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False);
if (p_icon.is_valid()) {
+ ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0);
+
Ref<Image> img = p_icon->duplicate();
img->convert(Image::FORMAT_RGBA8);
@@ -5449,7 +5481,9 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
#else
#ifdef XKB_ENABLED
- xkb_loaded = true;
+ bool xkb_loaded = true;
+ xkb_loaded_v05p = true;
+ xkb_loaded_v08p = true;
#endif
#endif
@@ -5476,6 +5510,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
r_error = OK;
+#ifdef SOWRAP_ENABLED
{
if (!XcursorImageCreate || !XcursorImageLoadCursor || !XcursorImageDestroy || !XcursorGetDefaultSize || !XcursorGetTheme || !XcursorLibraryLoadImage) {
// There's no API to check version, check if functions are available instead.
@@ -5484,6 +5519,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
return;
}
}
+#endif
for (int i = 0; i < CURSOR_MAX; i++) {
cursors[i] = None;
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 180362923b..70703d42c3 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -502,6 +502,7 @@ public:
virtual String keyboard_get_layout_language(int p_index) const override;
virtual String keyboard_get_layout_name(int p_index) const override;
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
+ virtual Key keyboard_get_label_from_physical(Key p_keycode) const override;
virtual void process_events() override;
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 93fa93b259..e5e0e53bfb 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;
@@ -326,6 +328,9 @@ public:
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
+ virtual Ref<Image> clipboard_get_image() const override;
+ virtual bool clipboard_has() const override;
+ virtual bool clipboard_has_image() const override;
virtual int get_screen_count() const override;
virtual int get_primary_screen() const override;
@@ -433,6 +438,7 @@ public:
virtual String keyboard_get_layout_language(int p_index) const override;
virtual String keyboard_get_layout_name(int p_index) const override;
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
+ virtual Key keyboard_get_label_from_physical(Key p_keycode) const override;
virtual void process_events() override;
virtual void force_process_and_drop_events() override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 5ccef68e7f..d64bb5211e 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -44,8 +44,10 @@
#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/os/keyboard.h"
+#include "drivers/png/png_driver_common.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 +1849,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_
@@ -2100,6 +2272,37 @@ String DisplayServerMacOS::clipboard_get() const {
return ret;
}
+Ref<Image> DisplayServerMacOS::clipboard_get_image() const {
+ Ref<Image> image;
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ NSString *result = [pasteboard availableTypeFromArray:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, NSPasteboardTypePNG, nil]];
+ if (!result) {
+ return image;
+ }
+ NSData *data = [pasteboard dataForType:result];
+ if (!data) {
+ return image;
+ }
+ NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:data];
+ NSData *pngData = [bitmap representationUsingType:NSPNGFileType properties:@{}];
+ image.instantiate();
+ PNGDriverCommon::png_to_image((const uint8_t *)pngData.bytes, pngData.length, false, image);
+ return image;
+}
+
+bool DisplayServerMacOS::clipboard_has() const {
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
+ NSDictionary *options = [NSDictionary dictionary];
+ return [pasteboard canReadObjectForClasses:classArray options:options];
+}
+
+bool DisplayServerMacOS::clipboard_has_image() const {
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ NSString *result = [pasteboard availableTypeFromArray:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, NSPasteboardTypePNG, nil]];
+ return result;
+}
+
int DisplayServerMacOS::get_screen_count() const {
_THREAD_SAFE_METHOD_
@@ -3085,14 +3288,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;
}
}
@@ -3499,6 +3702,17 @@ Key DisplayServerMacOS::keyboard_get_keycode_from_physical(Key p_keycode) const
return (Key)(KeyMappingMacOS::remap_key(macos_keycode, 0, false) | modifiers);
}
+Key DisplayServerMacOS::keyboard_get_label_from_physical(Key p_keycode) const {
+ if (p_keycode == Key::PAUSE || p_keycode == Key::NONE) {
+ return p_keycode;
+ }
+
+ Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;
+ Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK;
+ unsigned int macos_keycode = KeyMappingMacOS::unmap_key(keycode_no_mod);
+ return (Key)(KeyMappingMacOS::remap_key(macos_keycode, 0, true) | modifiers);
+}
+
void DisplayServerMacOS::process_events() {
_THREAD_SAFE_METHOD_
@@ -3609,40 +3823,46 @@ void DisplayServerMacOS::set_native_icon(const String &p_filename) {
void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) {
_THREAD_SAFE_METHOD_
- Ref<Image> img = p_icon;
- img = img->duplicate();
- img->convert(Image::FORMAT_RGBA8);
- NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:nullptr
- pixelsWide:img->get_width()
- pixelsHigh:img->get_height()
- bitsPerSample:8
- samplesPerPixel:4
- hasAlpha:YES
- isPlanar:NO
- colorSpaceName:NSDeviceRGBColorSpace
- bytesPerRow:img->get_width() * 4
- bitsPerPixel:32];
- ERR_FAIL_COND(imgrep == nil);
- uint8_t *pixels = [imgrep bitmapData];
+ if (p_icon.is_valid()) {
+ ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0);
- int len = img->get_width() * img->get_height();
- const uint8_t *r = img->get_data().ptr();
+ Ref<Image> img = p_icon->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
- /* Premultiply the alpha channel */
- for (int i = 0; i < len; i++) {
- uint8_t alpha = r[i * 4 + 3];
- pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
- pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
- pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
- pixels[i * 4 + 3] = alpha;
- }
+ NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes:nullptr
+ pixelsWide:img->get_width()
+ pixelsHigh:img->get_height()
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:img->get_width() * 4
+ bitsPerPixel:32];
+ ERR_FAIL_COND(imgrep == nil);
+ uint8_t *pixels = [imgrep bitmapData];
- NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())];
- ERR_FAIL_COND(nsimg == nil);
+ int len = img->get_width() * img->get_height();
+ const uint8_t *r = img->get_data().ptr();
- [nsimg addRepresentation:imgrep];
- [NSApp setApplicationIconImage:nsimg];
+ /* Premultiply the alpha channel */
+ for (int i = 0; i < len; i++) {
+ uint8_t alpha = r[i * 4 + 3];
+ pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
+ pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
+ pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
+ pixels[i * 4 + 3] = alpha;
+ }
+
+ NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())];
+ ERR_FAIL_COND(nsimg == nil);
+
+ [nsimg addRepresentation:imgrep];
+ [NSApp setApplicationIconImage:nsimg];
+ } else {
+ [NSApp setApplicationIconImage:nil];
+ }
}
DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) {
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 2d185db812..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>");
@@ -2442,11 +2452,10 @@ EditorExportPlatformMacOS::EditorExportPlatformMacOS() {
Ref<Image> img = memnew(Image);
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
- ImageLoaderSVG img_loader;
- img_loader.create_image_from_string(img, _macos_logo_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _macos_logo_svg, EDSCALE, upsample, false);
logo = ImageTexture::create_from_image(img);
- img_loader.create_image_from_string(img, _macos_run_icon_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _macos_run_icon_svg, EDSCALE, upsample, false);
run_icon = ImageTexture::create_from_image(img);
#endif
diff --git a/platform/macos/export/run_icon.svg b/platform/macos/export/run_icon.svg
index c7067bb4b6..647270ce22 100644
--- a/platform/macos/export/run_icon.svg
+++ b/platform/macos/export/run_icon.svg
@@ -1 +1 @@
-<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke-width:.93168;-inkscape-stroke:none" d="M4.418 1.055c-1.82 0-3.365 1.537-3.365 3.363v7.164c0 1.829 1.548 3.363 3.365 3.363h7.164c1.828 0 3.363-1.544 3.363-3.363V4.418c0-1.822-1.537-3.363-3.363-3.363H7.729Zm3.875 1.164h3.291c1.149 0 2.2 1.053 2.2 2.199v7.164c0 1.14-1.052 2.2-2.2 2.2h-2.15a8.884 8.884 0 0 1-.358-1.598c-.135.02-.487.082-.693.117a3.947 3.947 0 0 1-.049.004l-.008-.002a7.345 7.345 0 0 1-1.205-.004 7.114 7.114 0 0 1-.926-.139 6.057 6.057 0 0 1-.867-.271 3.843 3.843 0 0 1-.988-.566 3.214 3.214 0 0 1-.397-.378 2.8 2.8 0 0 1-.318-.441.558.558 0 0 1-.059-.424.564.564 0 0 1 .881-.299.56.56 0 0 1 .145.164c.083.138.188.26.312.362.096.082.2.158.307.224.12.075.243.142.371.201.285.139.583.247.89.319a5.35 5.35 0 0 0 1.282.158c.065 0 .129-.005.184-.006.056 0 .102-.005.148-.008l.096-.006c.114-.009.228-.02.31-.032.083-.013.11-.021.143-.028.099-.022.204-.058.327-.089a28.438 28.438 0 0 1-.06-1.929V8.53H6.887c.048-1.963.746-4.357 1.181-5.677.1-.293.184-.527.225-.633ZM4.973 5.03h.002a.562.562 0 0 1 .558.559v.805a.556.556 0 0 1-.558.558.56.56 0 0 1-.397-.162.565.565 0 0 1-.164-.396V5.59a.561.561 0 0 1 .559-.559Z"/><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke-linecap:round;-inkscape-stroke:none" d="M26.117 11.467c.008.11.014.225.022.328.022.283.052.565.088.846l.012.053c1.238-.252 2.448-.829 3.011-1.803a.6.6 0 1 0-1.039-.6c-.28.486-1.161.936-2.094 1.176z" transform="translate(-15.37 .357) scale(.93168)"/><g style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-opacity:1"><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:1.2;stroke-linecap:round;stroke-opacity:1;-inkscape-stroke:none" d="M27.836 5.585v.862" transform="translate(-15.37 .357) scale(.93168)"/><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-linecap:round;stroke-opacity:1;-inkscape-stroke:none" d="M27.836 4.984a.6.6 0 0 0-.6.6v.863a.6.6 0 0 0 .6.6.6.6 0 0 0 .6-.6v-.863a.6.6 0 0 0-.6-.6Z" transform="translate(-15.37 .357) scale(.93168)"/></g></svg>
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path fill="#e0e0e0" d="M4.418 1.055a3.364 3.364 0 0 0-3.364 3.364v7.164a3.364 3.364 0 0 0 3.364 3.364h7.164a3.364 3.364 0 0 0 3.364-3.364V4.418a3.364 3.364 0 0 0-3.364-3.364H7.729Zm3.875 1.164h3.291a2.2 2.2 0 0 1 2.2 2.2v7.164a2.2 2.2 0 0 1-2.2 2.2h-2.15a8.884 8.884 0 0 1-.358-1.598c-.135.02-.487.082-.693.117a7.345 7.345 0 0 1-1.254 0 7.114 7.114 0 0 1-.926-.139 6.057 6.057 0 0 1-.867-.271 3.843 3.843 0 0 1-.988-.566 3.214 3.214 0 0 1-.397-.378 2.8 2.8 0 0 1-.318-.441.56.56 0 0 1 .968-.56c.083.138.188.26.312.362.096.082.2.158.307.224.12.075.243.142.371.201.285.139.583.247.89.319a5.35 5.35 0 0 0 1.282.158c.065 0 .129-.005.184-.006.056 0 .102-.005.148-.008l.096-.006c.114-.009.228-.02.31-.032.083-.013.11-.021.143-.028.099-.022.204-.058.327-.089a28.438 28.438 0 0 1-.06-1.929V8.53H6.887c.048-1.963.746-4.357 1.181-5.677.1-.293.184-.527.225-.633ZM5.531 6.394a.56.56 0 0 1-1.118 0v-.9a.56.56 0 0 1 1.118 0Zm3.432 4.646.02.306c.02.264.049.527.082.788l.011.05c1.154-.235 2.281-.773 2.806-1.68a.56.56 0 1 0-.968-.56c-.261.454-1.082.873-1.951 1.097zM10 6.364a.56.56 0 0 0 1.118 0v-.9a.56.56 0 0 0-1.118 0z"/></svg>
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 0332fbf718..c92520b755 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
@@ -131,11 +132,11 @@ void EditorExportPlatformUWP::get_export_options(List<ExportOption> *r_options)
bool EditorExportPlatformUWP::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
#ifndef DEV_ENABLED
// We don't provide export templates for the UWP platform currently as it
- // has not been ported for Godot 4.0. This is skipped in DEV_ENABLED so that
+ // has not been ported for Godot 4. This is skipped in DEV_ENABLED so that
// contributors can still test the pipeline if/when we can build it again.
- r_error = "The UWP platform is currently not supported in Godot 4.0.\n";
+ r_error = "The UWP platform is currently not supported in Godot 4.\n";
return false;
-#endif
+#else
String err;
bool valid = false;
@@ -175,16 +176,17 @@ bool EditorExportPlatformUWP::has_valid_export_configuration(const Ref<EditorExp
}
return valid;
+#endif // DEV_ENABLED
}
bool EditorExportPlatformUWP::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
#ifndef DEV_ENABLED
// We don't provide export templates for the UWP platform currently as it
- // has not been ported for Godot 4.0. This is skipped in DEV_ENABLED so that
+ // has not been ported for Godot 4. This is skipped in DEV_ENABLED so that
// contributors can still test the pipeline if/when we can build it again.
- r_error = "The UWP platform is currently not supported in Godot 4.0.\n";
+ r_error = "The UWP platform is currently not supported in Godot 4.\n";
return false;
-#endif
+#else
String err;
bool valid = true;
@@ -258,6 +260,7 @@ bool EditorExportPlatformUWP::has_valid_project_configuration(const Ref<EditorEx
r_error = err;
return valid;
+#endif // DEV_ENABLED
}
Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
@@ -265,7 +268,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");
@@ -515,8 +518,7 @@ EditorExportPlatformUWP::EditorExportPlatformUWP() {
Ref<Image> img = memnew(Image);
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
- ImageLoaderSVG img_loader;
- img_loader.create_image_from_string(img, _uwp_logo_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _uwp_logo_svg, EDSCALE, upsample, false);
logo = ImageTexture::create_from_image(img);
#endif
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 951ce110e0..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
@@ -731,35 +732,40 @@ void DisplayServerWeb::send_window_event_callback(int p_notification) {
}
void DisplayServerWeb::set_icon(const Ref<Image> &p_icon) {
- ERR_FAIL_COND(p_icon.is_null());
- Ref<Image> icon = p_icon;
- if (icon->is_compressed()) {
- icon = icon->duplicate();
- ERR_FAIL_COND(icon->decompress() != OK);
- }
- if (icon->get_format() != Image::FORMAT_RGBA8) {
- if (icon == p_icon) {
+ if (p_icon.is_valid()) {
+ ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0);
+
+ Ref<Image> icon = p_icon;
+ if (icon->is_compressed()) {
icon = icon->duplicate();
+ ERR_FAIL_COND(icon->decompress() != OK);
+ }
+ if (icon->get_format() != Image::FORMAT_RGBA8) {
+ if (icon == p_icon) {
+ icon = icon->duplicate();
+ }
+ icon->convert(Image::FORMAT_RGBA8);
}
- icon->convert(Image::FORMAT_RGBA8);
- }
- png_image png_meta;
- memset(&png_meta, 0, sizeof png_meta);
- png_meta.version = PNG_IMAGE_VERSION;
- png_meta.width = icon->get_width();
- png_meta.height = icon->get_height();
- png_meta.format = PNG_FORMAT_RGBA;
+ png_image png_meta;
+ memset(&png_meta, 0, sizeof png_meta);
+ png_meta.version = PNG_IMAGE_VERSION;
+ png_meta.width = icon->get_width();
+ png_meta.height = icon->get_height();
+ png_meta.format = PNG_FORMAT_RGBA;
- PackedByteArray png;
- size_t len;
- PackedByteArray data = icon->get_data();
- ERR_FAIL_COND(!png_image_write_get_memory_size(png_meta, len, 0, data.ptr(), 0, nullptr));
+ PackedByteArray png;
+ size_t len;
+ PackedByteArray data = icon->get_data();
+ ERR_FAIL_COND(!png_image_write_get_memory_size(png_meta, len, 0, data.ptr(), 0, nullptr));
- png.resize(len);
- ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, png.ptrw(), &len, 0, data.ptr(), 0, nullptr));
+ png.resize(len);
+ ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, png.ptrw(), &len, 0, data.ptr(), 0, nullptr));
- godot_js_display_window_icon_set(png.ptr(), len);
+ godot_js_display_window_icon_set(png.ptr(), len);
+ } else {
+ godot_js_display_window_icon_set(nullptr, 0);
+ }
}
void DisplayServerWeb::_dispatch_input_event(const Ref<InputEvent> &p_event) {
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 38d7ed7fb6..38e2714d9f 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
@@ -359,17 +360,16 @@ Ref<Texture2D> EditorExportPlatformWeb::get_logo() const {
}
bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
- String err;
- bool valid = false;
- bool extensions = (bool)p_preset->get("variant/extensions_support");
-
#ifdef MODULE_MONO_ENABLED
- err += TTR("Exporting to Web is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target Web with C#/Mono instead.") + "\n";
- err += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n";
// Don't check for additional errors, as this particular error cannot be resolved.
- r_error = err;
+ r_error += TTR("Exporting to Web is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target Web with C#/Mono instead.") + "\n";
+ r_error += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n";
return false;
-#endif
+#else
+
+ String err;
+ bool valid = false;
+ bool extensions = (bool)p_preset->get("variant/extensions_support");
// Look for export templates (first official, and if defined custom templates).
bool dvalid = exists_export_template(_get_template_name(extensions, true), &err);
@@ -396,6 +396,7 @@ bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExp
}
return valid;
+#endif // !MODULE_MONO_ENABLED
}
bool EditorExportPlatformWeb::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
@@ -674,11 +675,10 @@ EditorExportPlatformWeb::EditorExportPlatformWeb() {
Ref<Image> img = memnew(Image);
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
- ImageLoaderSVG img_loader;
- img_loader.create_image_from_string(img, _web_logo_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _web_logo_svg, EDSCALE, upsample, false);
logo = ImageTexture::create_from_image(img);
- img_loader.create_image_from_string(img, _web_run_icon_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _web_run_icon_svg, EDSCALE, upsample, false);
run_icon = ImageTexture::create_from_image(img);
#endif
diff --git a/platform/web/export/run_icon.svg b/platform/web/export/run_icon.svg
index 494f53cb90..fa95e64e79 100644
--- a/platform/web/export/run_icon.svg
+++ b/platform/web/export/run_icon.svg
@@ -1 +1 @@
-<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M3.143 1 5.48 27.504 15.967 31l10.553-3.496L28.857 1ZM23.78 9.565H11.473l.275 3.308h11.759l-.911 9.937-6.556 1.808v.02h-.073l-6.61-1.828-.402-5.076h3.195l.234 2.552 3.583.97 3.595-.97.402-4.165H8.788L7.93 6.37h16.145Z" fill="#eb6428" style="fill:#e0e0e0;fill-opacity:1" transform="translate(.586 .586) scale(.46337)"/></svg>
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m2 1 1.09 12.357 4.9 1.63 4.9-1.63L13.98 1zm9.622 3.994h-5.74l.129 1.541h5.482l-.424 4.634-3.057.843v.01h-.033l-3.082-.853-.187-2.367h1.489l.11 1.19 1.67.452 1.676-.453.187-1.942h-5.21l-.4-4.546h7.527z" fill="#eb6428" style="fill:#e0e0e0;fill-opacity:1"/></svg>
diff --git a/platform/web/http_client_web.cpp b/platform/web/http_client_web.cpp
index 3e4ba5a2ae..ea9226a5a4 100644
--- a/platform/web/http_client_web.cpp
+++ b/platform/web/http_client_web.cpp
@@ -149,7 +149,15 @@ Error HTTPClientWeb::get_response_headers(List<String> *r_response) {
}
int64_t HTTPClientWeb::get_response_body_length() const {
- return godot_js_fetch_body_length_get(js_id);
+ // Body length cannot be consistently retrieved from the web.
+ // Reading the "content-length" value will return a meaningless value when the response is compressed,
+ // as reading will return uncompressed chunks in any case, resulting in a mismatch between the detected
+ // body size and the actual size returned by repeatedly calling read_response_body_chunk.
+ // Additionally, while "content-length" is considered a safe CORS header, "content-encoding" is not,
+ // so using the "content-encoding" to decide if "content-length" is meaningful is not an option either.
+ // We simply must accept the fact that browsers are awful when it comes to networking APIs.
+ // See GH-47597, and GH-79327.
+ return -1;
}
PackedByteArray HTTPClientWeb::read_response_body_chunk() {
diff --git a/platform/web/http_client_web.h b/platform/web/http_client_web.h
index bb9672ab82..4d3c457a7d 100644
--- a/platform/web/http_client_web.h
+++ b/platform/web/http_client_web.h
@@ -51,7 +51,6 @@ extern int godot_js_fetch_read_headers(int p_id, void (*parse_callback)(int p_si
extern int godot_js_fetch_read_chunk(int p_id, uint8_t *p_buf, int p_buf_size);
extern void godot_js_fetch_free(int p_id);
extern godot_js_fetch_state_t godot_js_fetch_state_get(int p_id);
-extern int godot_js_fetch_body_length_get(int p_id);
extern int godot_js_fetch_http_status_get(int p_id);
extern int godot_js_fetch_is_chunked(int p_id);
diff --git a/platform/web/js/libs/library_godot_display.js b/platform/web/js/libs/library_godot_display.js
index ea2a846f90..746f858923 100644
--- a/platform/web/js/libs/library_godot_display.js
+++ b/platform/web/js/libs/library_godot_display.js
@@ -568,16 +568,23 @@ const GodotDisplay = {
godot_js_display_window_icon_set__sig: 'vii',
godot_js_display_window_icon_set: function (p_ptr, p_len) {
let link = document.getElementById('-gd-engine-icon');
- if (link === null) {
- link = document.createElement('link');
- link.rel = 'icon';
- link.id = '-gd-engine-icon';
- document.head.appendChild(link);
- }
const old_icon = GodotDisplay.window_icon;
- const png = new Blob([GodotRuntime.heapSlice(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
- GodotDisplay.window_icon = URL.createObjectURL(png);
- link.href = GodotDisplay.window_icon;
+ if (p_ptr) {
+ if (link === null) {
+ link = document.createElement('link');
+ link.rel = 'icon';
+ link.id = '-gd-engine-icon';
+ document.head.appendChild(link);
+ }
+ const png = new Blob([GodotRuntime.heapSlice(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
+ GodotDisplay.window_icon = URL.createObjectURL(png);
+ link.href = GodotDisplay.window_icon;
+ } else {
+ if (link) {
+ link.remove();
+ }
+ GodotDisplay.window_icon = null;
+ }
if (old_icon) {
URL.revokeObjectURL(old_icon);
}
diff --git a/platform/web/js/libs/library_godot_fetch.js b/platform/web/js/libs/library_godot_fetch.js
index 1bb48bfd6a..4ef24903e3 100644
--- a/platform/web/js/libs/library_godot_fetch.js
+++ b/platform/web/js/libs/library_godot_fetch.js
@@ -50,22 +50,17 @@ const GodotFetch = {
return;
}
let chunked = false;
- let bodySize = -1;
response.headers.forEach(function (value, header) {
const v = value.toLowerCase().trim();
const h = header.toLowerCase().trim();
if (h === 'transfer-encoding' && v === 'chunked') {
chunked = true;
}
- if (h === 'content-length') {
- bodySize = parseInt(v, 10);
- }
});
obj.status = response.status;
obj.response = response;
obj.reader = response.body.getReader();
obj.chunked = chunked;
- obj.bodySize = bodySize;
},
onerror: function (id, err) {
@@ -87,7 +82,6 @@ const GodotFetch = {
reading: false,
status: 0,
chunks: [],
- bodySize: -1,
};
const id = IDHandler.add(obj);
const init = {
@@ -224,15 +218,6 @@ const GodotFetch = {
return p_buf_size - to_read;
},
- godot_js_fetch_body_length_get__sig: 'ii',
- godot_js_fetch_body_length_get: function (p_id) {
- const obj = IDHandler.get(p_id);
- if (!obj || !obj.response) {
- return -1;
- }
- return obj.bodySize;
- },
-
godot_js_fetch_is_chunked__sig: 'ii',
godot_js_fetch_is_chunked: function (p_id) {
const obj = IDHandler.get(p_id);
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 4138f53a9d..e32b377047 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -32,9 +32,11 @@
#include "os_windows.h"
+#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
+#include "drivers/png/png_driver_common.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 +44,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 +91,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 +218,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_
@@ -341,6 +469,68 @@ String DisplayServerWindows::clipboard_get() const {
return ret;
}
+Ref<Image> DisplayServerWindows::clipboard_get_image() const {
+ Ref<Image> image;
+ if (!windows.has(last_focused_window)) {
+ return image; // No focused window?
+ }
+ if (!OpenClipboard(windows[last_focused_window].hWnd)) {
+ ERR_FAIL_V_MSG(image, "Unable to open clipboard.");
+ }
+ UINT png_format = RegisterClipboardFormatA("PNG");
+ if (png_format && IsClipboardFormatAvailable(png_format)) {
+ HANDLE png_handle = GetClipboardData(png_format);
+ if (png_handle) {
+ size_t png_size = GlobalSize(png_handle);
+ uint8_t *png_data = (uint8_t *)GlobalLock(png_handle);
+ image.instantiate();
+
+ PNGDriverCommon::png_to_image(png_data, png_size, false, image);
+
+ GlobalUnlock(png_handle);
+ }
+ } else if (IsClipboardFormatAvailable(CF_DIB)) {
+ HGLOBAL mem = GetClipboardData(CF_DIB);
+ if (mem != NULL) {
+ BITMAPINFO *ptr = static_cast<BITMAPINFO *>(GlobalLock(mem));
+
+ if (ptr != NULL) {
+ BITMAPINFOHEADER *info = &ptr->bmiHeader;
+ PackedByteArray pba;
+
+ for (LONG y = info->biHeight - 1; y > -1; y--) {
+ for (LONG x = 0; x < info->biWidth; x++) {
+ tagRGBQUAD *rgbquad = ptr->bmiColors + (info->biWidth * y) + x;
+ pba.append(rgbquad->rgbRed);
+ pba.append(rgbquad->rgbGreen);
+ pba.append(rgbquad->rgbBlue);
+ pba.append(rgbquad->rgbReserved);
+ }
+ }
+ image.instantiate();
+ image->create_from_data(info->biWidth, info->biHeight, false, Image::Format::FORMAT_RGBA8, pba);
+
+ GlobalUnlock(mem);
+ }
+ }
+ }
+
+ CloseClipboard();
+
+ return image;
+}
+
+bool DisplayServerWindows::clipboard_has() const {
+ return (IsClipboardFormatAvailable(CF_TEXT) ||
+ IsClipboardFormatAvailable(CF_UNICODETEXT) ||
+ IsClipboardFormatAvailable(CF_OEMTEXT));
+}
+
+bool DisplayServerWindows::clipboard_has_image() const {
+ UINT png_format = RegisterClipboardFormatA("PNG");
+ return ((png_format && IsClipboardFormatAvailable(png_format)) || IsClipboardFormatAvailable(CF_DIB));
+}
+
typedef struct {
int count;
int screen;
@@ -618,7 +808,7 @@ Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const {
COLORREF col = GetPixel(dc, p.x, p.y);
if (col != CLR_INVALID) {
ReleaseDC(NULL, dc);
- return Color(float(col & 0x000000FF) / 256.0, float((col & 0x0000FF00) >> 8) / 256.0, float((col & 0x00FF0000) >> 16) / 256.0, 1.0);
+ return Color(float(col & 0x000000FF) / 255.0f, float((col & 0x0000FF00) >> 8) / 255.0f, float((col & 0x00FF0000) >> 16) / 255.0f, 1.0f);
}
ReleaseDC(NULL, dc);
}
@@ -2010,6 +2200,38 @@ Key DisplayServerWindows::keyboard_get_keycode_from_physical(Key p_keycode) cons
return (Key)(KeyMappingWindows::get_keysym(vk) | modifiers);
}
+Key DisplayServerWindows::keyboard_get_label_from_physical(Key p_keycode) const {
+ Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;
+ Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK);
+
+ if (keycode_no_mod == Key::PRINT ||
+ keycode_no_mod == Key::KP_ADD ||
+ keycode_no_mod == Key::KP_5 ||
+ (keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) {
+ return p_keycode;
+ }
+
+ unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod);
+ if (scancode == 0) {
+ return p_keycode;
+ }
+
+ Key keycode = KeyMappingWindows::get_keysym(MapVirtualKey(scancode, MAPVK_VSC_TO_VK));
+
+ HKL current_layout = GetKeyboardLayout(0);
+ static BYTE keyboard_state[256];
+ memset(keyboard_state, 0, 256);
+ wchar_t chars[256] = {};
+ UINT extended_code = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX);
+ if (ToUnicodeEx(extended_code, scancode, keyboard_state, chars, 255, 4, current_layout) > 0) {
+ String keysym = String::utf16((char16_t *)chars, 255);
+ if (!keysym.is_empty()) {
+ return fix_key_label(keysym[0], keycode) | modifiers;
+ }
+ }
+ return p_keycode;
+}
+
String _get_full_layout_name_from_registry(HKL p_layout) {
String id = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + String::num_int64((int64_t)p_layout, 16, false).lpad(8, "0");
String ret;
@@ -2194,55 +2416,65 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) {
void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
_THREAD_SAFE_METHOD_
- ERR_FAIL_COND(!p_icon.is_valid());
- if (icon != p_icon) {
- icon = p_icon->duplicate();
- if (icon->get_format() != Image::FORMAT_RGBA8) {
- icon->convert(Image::FORMAT_RGBA8);
+ if (p_icon.is_valid()) {
+ ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0);
+
+ Ref<Image> img = p_icon;
+ if (img != icon) {
+ img = img->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
}
- }
- int w = icon->get_width();
- int h = icon->get_height();
-
- // Create temporary bitmap buffer.
- int icon_len = 40 + h * w * 4;
- Vector<BYTE> v;
- v.resize(icon_len);
- BYTE *icon_bmp = v.ptrw();
-
- encode_uint32(40, &icon_bmp[0]);
- encode_uint32(w, &icon_bmp[4]);
- encode_uint32(h * 2, &icon_bmp[8]);
- encode_uint16(1, &icon_bmp[12]);
- encode_uint16(32, &icon_bmp[14]);
- encode_uint32(BI_RGB, &icon_bmp[16]);
- encode_uint32(w * h * 4, &icon_bmp[20]);
- encode_uint32(0, &icon_bmp[24]);
- encode_uint32(0, &icon_bmp[28]);
- encode_uint32(0, &icon_bmp[32]);
- encode_uint32(0, &icon_bmp[36]);
-
- uint8_t *wr = &icon_bmp[40];
- const uint8_t *r = icon->get_data().ptr();
-
- for (int i = 0; i < h; i++) {
- for (int j = 0; j < w; j++) {
- const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];
- uint8_t *wpx = &wr[(i * w + j) * 4];
- wpx[0] = rpx[2];
- wpx[1] = rpx[1];
- wpx[2] = rpx[0];
- wpx[3] = rpx[3];
+
+ int w = img->get_width();
+ int h = img->get_height();
+
+ // Create temporary bitmap buffer.
+ int icon_len = 40 + h * w * 4;
+ Vector<BYTE> v;
+ v.resize(icon_len);
+ BYTE *icon_bmp = v.ptrw();
+
+ encode_uint32(40, &icon_bmp[0]);
+ encode_uint32(w, &icon_bmp[4]);
+ encode_uint32(h * 2, &icon_bmp[8]);
+ encode_uint16(1, &icon_bmp[12]);
+ encode_uint16(32, &icon_bmp[14]);
+ encode_uint32(BI_RGB, &icon_bmp[16]);
+ encode_uint32(w * h * 4, &icon_bmp[20]);
+ encode_uint32(0, &icon_bmp[24]);
+ encode_uint32(0, &icon_bmp[28]);
+ encode_uint32(0, &icon_bmp[32]);
+ encode_uint32(0, &icon_bmp[36]);
+
+ uint8_t *wr = &icon_bmp[40];
+ const uint8_t *r = img->get_data().ptr();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];
+ uint8_t *wpx = &wr[(i * w + j) * 4];
+ wpx[0] = rpx[2];
+ wpx[1] = rpx[1];
+ wpx[2] = rpx[0];
+ wpx[3] = rpx[3];
+ }
}
- }
- HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
+ HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
+ ERR_FAIL_COND(!hicon);
+
+ icon = img;
- // Set the icon for the window.
- SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);
+ // Set the icon for the window.
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);
- // Set the icon in the task manager (should we do this?).
- SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);
+ // Set the icon in the task manager (should we do this?).
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);
+ } else {
+ icon = Ref<Image>();
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, 0);
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, 0);
+ }
}
void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
@@ -3606,6 +3838,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} break;
case WM_DESTROY: {
Input::get_singleton()->flush_buffered_events();
+ if (window_mouseover_id == window_id) {
+ window_mouseover_id = INVALID_WINDOW_ID;
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
} break;
case WM_SETCURSOR: {
if (LOWORD(lParam) == HTCLIENT) {
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 7228de7d31..59c4442604 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;
@@ -520,6 +522,9 @@ public:
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
+ virtual Ref<Image> clipboard_get_image() const override;
+ virtual bool clipboard_has() const override;
+ virtual bool clipboard_has_image() const override;
virtual int get_screen_count() const override;
virtual int get_primary_screen() const override;
@@ -623,6 +628,7 @@ public:
virtual String keyboard_get_layout_language(int p_index) const override;
virtual String keyboard_get_layout_name(int p_index) const override;
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
+ virtual Key keyboard_get_label_from_physical(Key p_keycode) const override;
virtual int tablet_get_driver_count() const override;
virtual String tablet_get_driver_name(int p_driver) const override;
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index b521a649be..0ef07c3275 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -1011,11 +1011,10 @@ EditorExportPlatformWindows::EditorExportPlatformWindows() {
Ref<Image> img = memnew(Image);
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
- ImageLoaderSVG img_loader;
- img_loader.create_image_from_string(img, _windows_logo_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _windows_logo_svg, EDSCALE, upsample, false);
set_logo(ImageTexture::create_from_image(img));
- img_loader.create_image_from_string(img, _windows_run_icon_svg, EDSCALE, upsample, false);
+ ImageLoaderSVG::create_image_from_string(img, _windows_run_icon_svg, EDSCALE, upsample, false);
run_icon = ImageTexture::create_from_image(img);
#endif
diff --git a/platform/windows/export/run_icon.svg b/platform/windows/export/run_icon.svg
index 0897276ef7..6a18433ed2 100644
--- a/platform/windows/export/run_icon.svg
+++ b/platform/windows/export/run_icon.svg
@@ -1 +1 @@
-<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m1.095 2.997 5.66-.78v5.469h-5.66zm0 10.006 5.66.78v-5.4h-5.66zm6.282.863 7.528 1.04V8.381H7.377Zm0-11.732v5.552h7.528V1.095Z" fill="#00abed" style="stroke-width:.460341;fill:#e0e0e0;fill-opacity:1"/></svg>
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m1.095 2.997 5.66-.78v5.469h-5.66zm0 10.006 5.66.78v-5.4h-5.66zm6.282.863 7.528 1.04V8.381H7.377Zm0-11.732v5.552h7.528V1.095Z" fill="#00abed" style="fill:#e0e0e0;fill-opacity:1"/></svg>
diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp
index 0334bdd973..d3972c7bbc 100644
--- a/platform/windows/gl_manager_windows.cpp
+++ b/platform/windows/gl_manager_windows.cpp
@@ -53,6 +53,7 @@
#if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
#define wglGetProcAddress (void *)wglGetProcAddress
+#define GetProcAddress (void *)GetProcAddress
#endif
typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *);
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 48445496aa..e7003ab98b 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -41,7 +41,7 @@ void Camera2D::_update_scroll() {
if (Engine::get_singleton()->is_editor_hint()) {
queue_redraw();
// Only set viewport transform when not bound to the main viewport.
- if (get_viewport() == get_tree()->get_edited_scene_root()->get_viewport()) {
+ if (get_tree()->get_edited_scene_root() && get_viewport() == get_tree()->get_edited_scene_root()->get_viewport()) {
return;
}
}
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 078a73c583..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) {
@@ -149,7 +167,7 @@ void GPUParticles2D::set_trail_enabled(bool p_enabled) {
}
void GPUParticles2D::set_trail_lifetime(double p_seconds) {
- ERR_FAIL_COND(p_seconds < 0.001);
+ ERR_FAIL_COND(p_seconds < 0.01);
trail_lifetime = p_seconds;
RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_lifetime);
queue_redraw();
@@ -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_link_2d.cpp b/scene/2d/navigation_link_2d.cpp
index 3664040e7b..95798b6856 100644
--- a/scene/2d/navigation_link_2d.cpp
+++ b/scene/2d/navigation_link_2d.cpp
@@ -182,15 +182,7 @@ void NavigationLink2D::set_enabled(bool p_enabled) {
enabled = p_enabled;
- if (!is_inside_tree()) {
- return;
- }
-
- if (!enabled) {
- NavigationServer2D::get_singleton()->link_set_map(link, RID());
- } else {
- NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map());
- }
+ NavigationServer3D::get_singleton()->link_set_enabled(link, enabled);
#ifdef DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) {
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 693e03e1d4..670a2c641c 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"
@@ -43,15 +42,7 @@ void NavigationRegion2D::set_enabled(bool p_enabled) {
enabled = p_enabled;
- if (!is_inside_tree()) {
- return;
- }
-
- if (!enabled) {
- NavigationServer2D::get_singleton()->region_set_map(region, RID());
- } else {
- NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
- }
+ NavigationServer2D::get_singleton()->region_set_enabled(region, enabled);
#ifdef DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_navigation_enabled()) {
@@ -161,17 +152,7 @@ bool NavigationRegion2D::_edit_is_selected_on_click(const Point2 &p_point, doubl
void NavigationRegion2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- if (enabled) {
- NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], get_world_2d()->get_navigation_map());
- NavigationServer2D::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
- }
- }
- }
- current_global_transform = get_global_transform();
- NavigationServer2D::get_singleton()->region_set_transform(region, current_global_transform);
+ _region_enter_navigation_map();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -179,30 +160,11 @@ void NavigationRegion2D::_notification(int p_what) {
} break;
case NOTIFICATION_EXIT_TREE: {
- NavigationServer2D::get_singleton()->region_set_map(region, RID());
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], RID());
- }
- }
+ _region_exit_navigation_map();
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
set_physics_process_internal(false);
- if (is_inside_tree()) {
- Transform2D new_global_transform = get_global_transform();
- if (current_global_transform != new_global_transform) {
- current_global_transform = new_global_transform;
- NavigationServer2D::get_singleton()->region_set_transform(region, current_global_transform);
- queue_redraw();
-
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
- }
- }
- }
- }
} break;
case NOTIFICATION_DRAW: {
@@ -222,14 +184,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();
@@ -240,6 +202,30 @@ Ref<NavigationPolygon> NavigationRegion2D::get_navigation_polygon() const {
return navigation_polygon;
}
+void NavigationRegion2D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
+ map_override = p_navigation_map;
+
+ NavigationServer2D::get_singleton()->region_set_map(region, map_override);
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], map_override);
+ }
+ }
+}
+
+RID NavigationRegion2D::get_navigation_map() const {
+ if (map_override.is_valid()) {
+ return map_override;
+ } else if (is_inside_tree()) {
+ return get_world_2d()->get_navigation_map();
+ }
+ return RID();
+}
+
void NavigationRegion2D::_navigation_polygon_changed() {
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) {
queue_redraw();
@@ -277,6 +263,9 @@ void NavigationRegion2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion2D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationRegion2D::is_enabled);
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationRegion2D::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationRegion2D::get_navigation_map);
+
ClassDB::bind_method(D_METHOD("set_use_edge_connections", "enabled"), &NavigationRegion2D::set_use_edge_connections);
ClassDB::bind_method(D_METHOD("get_use_edge_connections"), &NavigationRegion2D::get_use_edge_connections);
@@ -416,7 +405,11 @@ void NavigationRegion2D::_update_avoidance_constrain() {
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle_rid, new_obstacle_outline);
NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(obstacle_rid, avoidance_layers);
if (is_inside_tree()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(obstacle_rid, get_world_2d()->get_navigation_map());
+ if (map_override.is_valid()) {
+ NavigationServer2D::get_singleton()->obstacle_set_map(obstacle_rid, map_override);
+ } else {
+ NavigationServer2D::get_singleton()->obstacle_set_map(obstacle_rid, get_world_2d()->get_navigation_map());
+ }
NavigationServer2D::get_singleton()->obstacle_set_position(obstacle_rid, get_global_position());
}
}
@@ -472,6 +465,68 @@ bool NavigationRegion2D::get_avoidance_layer_value(int p_layer_number) const {
return get_avoidance_layers() & (1 << (p_layer_number - 1));
}
+void NavigationRegion2D::_region_enter_navigation_map() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (enabled) {
+ if (map_override.is_valid()) {
+ NavigationServer2D::get_singleton()->region_set_map(region, map_override);
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], map_override);
+ }
+ }
+ } else {
+ NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], get_world_2d()->get_navigation_map());
+ }
+ }
+ }
+ }
+
+ current_global_transform = get_global_transform();
+ NavigationServer2D::get_singleton()->region_set_transform(region, current_global_transform);
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ NavigationServer2D::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
+ }
+ }
+
+ queue_redraw();
+}
+
+void NavigationRegion2D::_region_exit_navigation_map() {
+ NavigationServer2D::get_singleton()->region_set_map(region, RID());
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], RID());
+ }
+ }
+}
+
+void NavigationRegion2D::_region_update_transform() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ Transform2D new_global_transform = get_global_transform();
+ if (current_global_transform != new_global_transform) {
+ current_global_transform = new_global_transform;
+ NavigationServer2D::get_singleton()->region_set_transform(region, current_global_transform);
+ for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
+ if (constrain_avoidance_obstacles[i].is_valid()) {
+ NavigationServer2D::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
+ }
+ }
+ }
+
+ queue_redraw();
+}
+
#ifdef DEBUG_ENABLED
void NavigationRegion2D::_update_debug_mesh() {
Vector<Vector2> navigation_polygon_vertices = navigation_polygon->get_vertices();
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index 642f1663cd..0a48b10f47 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -40,6 +40,7 @@ class NavigationRegion2D : public Node2D {
bool use_edge_connections = true;
RID region;
+ RID map_override;
uint32_t navigation_layers = 1;
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
@@ -79,6 +80,9 @@ public:
void set_enabled(bool p_enabled);
bool is_enabled() const;
+ void set_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
+
void set_use_edge_connections(bool p_enabled);
bool get_use_edge_connections() const;
@@ -115,6 +119,9 @@ public:
private:
void _update_avoidance_constrain();
+ void _region_enter_navigation_map();
+ void _region_exit_navigation_map();
+ void _region_update_transform();
};
#endif // NAVIGATION_REGION_2D_H
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..2dcf87ba81 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();
}
@@ -432,10 +429,7 @@ struct _RigidBody2DInOut {
int local_shape = 0;
};
-void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) {
- lock_callback();
-
- set_block_transform_notify(true); // don't want notify (would feedback loop)
+void RigidBody2D::_sync_body_state(PhysicsDirectBodyState2D *p_state) {
if (!freeze || freeze_mode != FREEZE_MODE_KINEMATIC) {
set_global_transform(p_state->get_transform());
}
@@ -447,9 +441,17 @@ void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) {
sleeping = p_state->is_sleeping();
emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed);
}
+}
+
+void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) {
+ lock_callback();
+
+ set_block_transform_notify(true); // don't want notify (would feedback loop)
+ _sync_body_state(p_state);
GDVIRTUAL_CALL(_integrate_forces, p_state);
+ _sync_body_state(p_state);
set_block_transform_notify(false); // want it back
if (contact_monitor) {
@@ -651,15 +653,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/physics_body_2d.h b/scene/2d/physics_body_2d.h
index c4eb77d861..f5448ead40 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -212,6 +212,8 @@ private:
static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state);
void _body_state_changed(PhysicsDirectBodyState2D *p_state);
+ void _sync_body_state(PhysicsDirectBodyState2D *p_state);
+
protected:
void _notification(int p_what);
static void _bind_methods();
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/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index c31b42beba..350fdad7d7 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -309,8 +309,8 @@ void Bone2D::_notification(int p_what) {
#ifdef TOOLS_ENABLED
bool Bone2D::_editor_get_bone_shape(Vector<Vector2> *p_shape, Vector<Vector2> *p_outline_shape, Bone2D *p_other_bone) {
- int bone_width = EDITOR_GET("editors/2d/bone_width");
- int bone_outline_width = EDITOR_GET("editors/2d/bone_outline_size");
+ float bone_width = EDITOR_GET("editors/2d/bone_width");
+ float bone_outline_width = EDITOR_GET("editors/2d/bone_outline_size");
if (!is_inside_tree()) {
return false; //may have been removed
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/modules/mono/mono_gd/android_mono_config.h b/scene/2d/tile_map.compat.inc
index bb9c2a5d5b..49e2bf6f0b 100644
--- a/modules/mono/mono_gd/android_mono_config.h
+++ b/scene/2d/tile_map.compat.inc
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* android_mono_config.h */
+/* object.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,16 +28,18 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifndef ANDROID_MONO_CONFIG_H
-#define ANDROID_MONO_CONFIG_H
+#ifndef DISABLE_DEPRECATED
-#ifdef ANDROID_ENABLED
+#include "core/object/object.h"
-#include "core/string/ustring.h"
+#include "core/object/class_db.h"
-// This function is defined in an auto-generated source file
-String get_godot_android_mono_config();
+Rect2i TileMap::_get_used_rect_bind_compat_78328() {
+ return get_used_rect();
+}
-#endif // ANDROID_ENABLED
+void TileMap::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_used_rect"), &TileMap::_get_used_rect_bind_compat_78328);
+}
-#endif // ANDROID_MONO_CONFIG_H
+#endif
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 5ea751f089..1836cc20b6 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 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);
+Vector2i TileMapLayer::_coords_to_quadrant_coords(const Vector2i &p_coords) const {
+ int quad_size = get_effective_quadrant_size();
- // 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,8 +243,17 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
ci = prev_ci;
}
+ // Random animation offset.
+ 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(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, E_cell.key - tile_position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, get_self_modulate(), tile_data);
+ 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++) {
@@ -1327,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;
}
@@ -1342,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) {
@@ -1386,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()) {
@@ -1395,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)) {
@@ -1427,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);
}
@@ -1436,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) {
- 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, 0.0);
-
- 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) {
@@ -1611,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)) {
@@ -1637,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);
@@ -1688,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;
@@ -1726,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));
@@ -1754,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();
@@ -1802,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)) {
@@ -1822,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;
}
}
}
@@ -1850,7 +607,39 @@ 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();
+ // Set the default NavigationPolygon cell_size on the new map as a mismatch causes an error.
+ NavigationServer2D::get_singleton()->map_set_cell_size(new_layer_map, 1.0);
+ 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) {
@@ -1865,18 +654,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;
@@ -1901,10 +691,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)) {
@@ -1924,7 +714,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++) {
@@ -1976,37 +766,26 @@ 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();
while (q_list_element) {
TileMapQuadrant &q = *q_list_element->self();
-
- // 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);
- if (node) {
- node->queue_free();
- }
- }
- }
-
- q.scenes.clear();
+ _scenes_cleanup_quadrant(&q);
// 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)) {
@@ -2024,13 +803,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();
}
}
@@ -2041,11 +820,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();
}
@@ -2054,7 +833,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()) {
@@ -2063,9 +843,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)) {
@@ -2093,7 +873,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);
}
@@ -2102,249 +882,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;
+ 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_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(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();
}
@@ -2355,8 +937,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()) {
@@ -2372,7 +954,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()) {
@@ -2391,7 +973,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) {
@@ -2404,19 +986,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);
}
}
@@ -2424,13 +1007,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;
@@ -2438,7 +1021,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));
}
}
}
@@ -2452,7 +1035,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;
@@ -2491,10 +1074,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;
@@ -2505,14 +1088,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>();
}
@@ -2527,9 +1150,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);
@@ -2543,7 +1166,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)) {
@@ -2559,12 +1182,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;
@@ -2575,11 +1199,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);
@@ -2588,16 +1212,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;
@@ -2620,7 +1244,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);
@@ -2628,16 +1252,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) {
@@ -2655,54 +1279,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);
@@ -2714,31 +1339,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);
@@ -2753,11 +1379,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);
@@ -2771,7 +1397,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);
@@ -2780,18 +1406,628 @@ 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.
+ clear_instantiated_scenes();
+ 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_instantiated_scenes();
+ 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;
@@ -2800,16 +2036,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);
@@ -2823,15 +2059,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;
@@ -2841,16 +2077,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);
@@ -2864,246 +2100,1236 @@ 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() {
+ Ref<TileSet> tileset = tile_map_node->get_tileset();
+ ERR_FAIL_COND_MSG(tileset.is_null(), "Cannot call fix_invalid_tiles() on a TileMap without a valid TileSet.");
+
+ RBSet<Vector2i> coords;
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ TileSetSource *source = *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.get_id()));
+}
+
+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.get_id()));
+}
+
+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
@@ -3111,15 +3337,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;
@@ -3132,12 +3363,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();
}
@@ -3160,7 +3395,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;
@@ -3172,7 +3408,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();
@@ -3199,7 +3435,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;
@@ -3223,7 +3459,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;
@@ -3232,7 +3468,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:
@@ -3254,7 +3490,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));
@@ -3278,7 +3514,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) {
@@ -3287,7 +3523,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) {
@@ -3309,7 +3545,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) {
@@ -3318,7 +3554,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) {
@@ -3330,7 +3566,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;
@@ -3340,7 +3576,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;
@@ -3395,8 +3631,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);
@@ -3404,7 +3640,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;
@@ -3533,7 +3769,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) {
@@ -3843,63 +4079,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 ---
@@ -3907,13 +4103,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();
}
}
@@ -3922,14 +4113,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();
}
}
@@ -3938,46 +4123,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();
}
}
@@ -4075,15 +4238,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;
}
@@ -4091,8 +4254,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;
}
@@ -4103,8 +4266,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;
}
@@ -4142,6 +4305,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);
@@ -4151,9 +4321,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));
@@ -4203,9 +4370,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);
@@ -4213,9 +4380,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();
}
@@ -4232,13 +4401,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 3c135d1317..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,30 +370,36 @@ 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;
void set_quadrant_size(int p_size);
int get_quadrant_size() const;
- static void 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 = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr);
+ static void 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 = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr, real_t p_animation_offset = 0.0);
// Layers management.
int get_layers_count() const;
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 4ac81d63b6..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 {
@@ -181,7 +199,7 @@ void GPUParticles3D::set_trail_enabled(bool p_enabled) {
}
void GPUParticles3D::set_trail_lifetime(double p_seconds) {
- ERR_FAIL_COND(p_seconds < 0.001);
+ ERR_FAIL_COND(p_seconds < 0.01);
trail_lifetime = p_seconds;
RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_lifetime);
}
@@ -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_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index 2263d38d6c..70416ca93b 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -291,15 +291,7 @@ void NavigationLink3D::set_enabled(bool p_enabled) {
enabled = p_enabled;
- if (!is_inside_tree()) {
- return;
- }
-
- if (enabled) {
- NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map());
- } else {
- NavigationServer3D::get_singleton()->link_set_map(link, RID());
- }
+ NavigationServer3D::get_singleton()->link_set_enabled(link, enabled);
#ifdef DEBUG_ENABLED
if (debug_instance.is_valid() && debug_mesh.is_valid()) {
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 4c2f56b7b3..8d66f7ebeb 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"
@@ -41,15 +40,7 @@ void NavigationRegion3D::set_enabled(bool p_enabled) {
enabled = p_enabled;
- if (!is_inside_tree()) {
- return;
- }
-
- if (!enabled) {
- NavigationServer3D::get_singleton()->region_set_map(region, RID());
- } else {
- NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
- }
+ NavigationServer3D::get_singleton()->region_set_enabled(region, enabled);
#ifdef DEBUG_ENABLED
if (debug_instance.is_valid()) {
@@ -169,17 +160,7 @@ RID NavigationRegion3D::get_region_rid() const {
void NavigationRegion3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- if (enabled) {
- NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
- }
- current_global_transform = get_global_transform();
- NavigationServer3D::get_singleton()->region_set_transform(region, current_global_transform);
-
-#ifdef DEBUG_ENABLED
- if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
- _update_debug_mesh();
- }
-#endif // DEBUG_ENABLED
+ _region_enter_navigation_map();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -188,31 +169,11 @@ void NavigationRegion3D::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
set_physics_process_internal(false);
- if (is_inside_tree()) {
- Transform3D new_global_transform = get_global_transform();
- if (current_global_transform != new_global_transform) {
- current_global_transform = new_global_transform;
- NavigationServer3D::get_singleton()->region_set_transform(region, current_global_transform);
-#ifdef DEBUG_ENABLED
- if (debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform);
- }
-#endif // DEBUG_ENABLED
- }
- }
+ _region_update_transform();
} break;
case NOTIFICATION_EXIT_TREE: {
- NavigationServer3D::get_singleton()->region_set_map(region, RID());
-
-#ifdef DEBUG_ENABLED
- if (debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_visible(debug_instance, false);
- }
- if (debug_edge_connections_instance.is_valid()) {
- RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
- }
-#endif // DEBUG_ENABLED
+ _region_exit_navigation_map();
} break;
}
}
@@ -223,13 +184,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);
@@ -260,6 +221,25 @@ Ref<NavigationMesh> NavigationRegion3D::get_navigation_mesh() const {
return navigation_mesh;
}
+void NavigationRegion3D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
+ map_override = p_navigation_map;
+
+ NavigationServer3D::get_singleton()->region_set_map(region, map_override);
+}
+
+RID NavigationRegion3D::get_navigation_map() const {
+ if (map_override.is_valid()) {
+ return map_override;
+ } else if (is_inside_tree()) {
+ return get_world_3d()->get_navigation_map();
+ }
+ return RID();
+}
+
struct BakeThreadsArgs {
NavigationRegion3D *nav_region = nullptr;
Ref<NavigationMeshSourceGeometryData3D> source_geometry_data;
@@ -273,11 +253,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);
}
}
@@ -330,6 +318,9 @@ void NavigationRegion3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion3D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationRegion3D::is_enabled);
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationRegion3D::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationRegion3D::get_navigation_map);
+
ClassDB::bind_method(D_METHOD("set_use_edge_connections", "enabled"), &NavigationRegion3D::set_use_edge_connections);
ClassDB::bind_method(D_METHOD("get_use_edge_connections"), &NavigationRegion3D::get_use_edge_connections);
@@ -397,6 +388,58 @@ void NavigationRegion3D::_navigation_map_changed(RID p_map) {
}
#endif // DEBUG_ENABLED
+void NavigationRegion3D::_region_enter_navigation_map() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (enabled) {
+ if (map_override.is_valid()) {
+ NavigationServer3D::get_singleton()->region_set_map(region, map_override);
+ } else {
+ NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
+ }
+ }
+
+ current_global_transform = get_global_transform();
+ NavigationServer3D::get_singleton()->region_set_transform(region, current_global_transform);
+
+#ifdef DEBUG_ENABLED
+ if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
+ _update_debug_mesh();
+ }
+#endif // DEBUG_ENABLED
+}
+
+void NavigationRegion3D::_region_exit_navigation_map() {
+ NavigationServer3D::get_singleton()->region_set_map(region, RID());
+#ifdef DEBUG_ENABLED
+ if (debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_instance, false);
+ }
+ if (debug_edge_connections_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_edge_connections_instance, false);
+ }
+#endif // DEBUG_ENABLED
+}
+
+void NavigationRegion3D::_region_update_transform() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ Transform3D new_global_transform = get_global_transform();
+ if (current_global_transform != new_global_transform) {
+ current_global_transform = new_global_transform;
+ NavigationServer3D::get_singleton()->region_set_transform(region, current_global_transform);
+#ifdef DEBUG_ENABLED
+ if (debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform);
+ }
+#endif // DEBUG_ENABLED
+ }
+}
+
NavigationRegion3D::NavigationRegion3D() {
set_notify_transform(true);
@@ -418,7 +461,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/navigation_region_3d.h b/scene/3d/navigation_region_3d.h
index 84b57d064f..e41d07f4cf 100644
--- a/scene/3d/navigation_region_3d.h
+++ b/scene/3d/navigation_region_3d.h
@@ -41,6 +41,7 @@ class NavigationRegion3D : public Node3D {
bool use_edge_connections = true;
RID region;
+ RID map_override;
uint32_t navigation_layers = 1;
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
@@ -77,6 +78,9 @@ public:
void set_enabled(bool p_enabled);
bool is_enabled() const;
+ void set_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
+
void set_use_edge_connections(bool p_enabled);
bool get_use_edge_connections() const;
@@ -106,6 +110,11 @@ public:
NavigationRegion3D();
~NavigationRegion3D();
+
+private:
+ void _region_enter_navigation_map();
+ void _region_exit_navigation_map();
+ void _region_update_transform();
};
#endif // NAVIGATION_REGION_3D_H
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..cf1b865b19 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();
}
@@ -487,10 +484,7 @@ struct _RigidBodyInOut {
int local_shape = 0;
};
-void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
- lock_callback();
-
- set_ignore_transform_notification(true);
+void RigidBody3D::_sync_body_state(PhysicsDirectBodyState3D *p_state) {
set_global_transform(p_state->get_transform());
linear_velocity = p_state->get_linear_velocity();
@@ -502,9 +496,17 @@ void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
sleeping = p_state->is_sleeping();
emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed);
}
+}
+
+void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
+ lock_callback();
+
+ set_ignore_transform_notification(true);
+ _sync_body_state(p_state);
GDVIRTUAL_CALL(_integrate_forces, p_state);
+ _sync_body_state(p_state);
set_ignore_transform_notification(false);
_on_transform_changed();
@@ -726,15 +728,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();
}
@@ -2920,25 +2920,28 @@ void PhysicalBone3D::_notification(int p_what) {
}
}
+void PhysicalBone3D::_sync_body_state(PhysicsDirectBodyState3D *p_state) {
+ set_global_transform(p_state->get_transform());
+ linear_velocity = p_state->get_linear_velocity();
+ angular_velocity = p_state->get_angular_velocity();
+}
+
void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
if (!simulate_physics || !_internal_simulate_physics) {
return;
}
- linear_velocity = p_state->get_linear_velocity();
- angular_velocity = p_state->get_angular_velocity();
+ set_ignore_transform_notification(true);
+ _sync_body_state(p_state);
GDVIRTUAL_CALL(_integrate_forces, p_state);
- /// Update bone transform.
-
- Transform3D global_transform(p_state->get_transform());
-
- set_ignore_transform_notification(true);
- set_global_transform(global_transform);
+ _sync_body_state(p_state);
set_ignore_transform_notification(false);
_on_transform_changed();
+ Transform3D global_transform(p_state->get_transform());
+
// Update skeleton
if (parent_skeleton) {
if (-1 != bone_id) {
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index d141c1aaa2..9798fc4845 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -222,6 +222,8 @@ private:
void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape);
static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state);
+ void _sync_body_state(PhysicsDirectBodyState3D *p_state);
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -692,6 +694,7 @@ protected:
static void _bind_methods();
private:
+ void _sync_body_state(PhysicsDirectBodyState3D *p_state);
static Skeleton3D *find_skeleton_parent(Node *p_parent);
void _update_joint_offset();
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..715d8a5bc1 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -1010,6 +1010,7 @@ double AnimationNodeTransition::_process(double p_time, bool p_seek, bool p_is_e
}
double rem = 0.0;
+ double abs_time = Math::abs(p_time);
if (sync) {
for (int i = 0; i < get_input_count(); i++) {
@@ -1024,9 +1025,9 @@ double AnimationNodeTransition::_process(double p_time, bool p_seek, bool p_is_e
rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true, p_test_only);
if (p_seek) {
- cur_time = p_time;
+ cur_time = abs_time;
} else {
- cur_time += p_time;
+ cur_time += abs_time;
}
if (input_data[cur_current_index].auto_advance && rem <= xfade_time) {
@@ -1058,10 +1059,10 @@ double AnimationNodeTransition::_process(double p_time, bool p_seek, bool p_is_e
blend_input(cur_prev_index, p_time, use_blend && p_seek, p_is_external_seeking, blend, FILTER_IGNORE, true, p_test_only);
if (p_seek) {
- cur_time = p_time;
+ cur_time = abs_time;
} else {
- cur_time += p_time;
- cur_prev_xfading -= p_time;
+ cur_time += abs_time;
+ cur_prev_xfading -= abs_time;
if (cur_prev_xfading < 0) {
set_parameter(prev_index, -1);
}
@@ -1142,7 +1143,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 {
@@ -1178,8 +1179,6 @@ void AnimationNodeBlendTree::get_child_nodes(List<ChildNode> *r_child_nodes) {
ns.push_back(E.key);
}
- ns.sort_custom<StringName::AlphCompare>();
-
for (int i = 0; i < ns.size(); i++) {
ChildNode cn;
cn.name = ns[i];
@@ -1206,7 +1205,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 +1230,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 +1244,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"));
@@ -1435,7 +1434,6 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons
for (const KeyValue<StringName, Node> &E : nodes) {
names.push_back(E.key);
}
- names.sort_custom<StringName::AlphCompare>();
for (const StringName &E : names) {
String prop_name = E;
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 2139af8a96..bf95c211f6 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -388,7 +388,7 @@ class AnimationNodeBlendTree : public AnimationRootNode {
Vector<StringName> connections;
};
- HashMap<StringName, Node> nodes;
+ RBMap<StringName, Node, StringName::AlphCompare> nodes;
Vector2 graph_offset;
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 96e5da5a40..b32b04655d 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -284,10 +284,6 @@ bool Tween::step(double p_delta) {
return false;
}
- if (!running) {
- return true;
- }
-
if (is_bound) {
Node *node = get_bound_node();
if (node) {
@@ -299,6 +295,10 @@ bool Tween::step(double p_delta) {
}
}
+ if (!running) {
+ return true;
+ }
+
if (!started) {
if (tweeners.is_empty()) {
String tween_id;
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index 391388fc04..4b097412d6 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -163,6 +163,7 @@ Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Arra
live_editor->_res_set_func(p_args[0], p_args[1], p_args[2]);
} else if (p_msg == "live_node_call") {
+ ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
LocalVector<Variant> args;
LocalVector<Variant *> argptrs;
args.resize(p_args.size() - 2);
@@ -171,11 +172,10 @@ Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Arra
args[i] = p_args[i + 2];
argptrs[i] = &args[i];
}
- live_editor->_node_call_func(p_args[0], p_args[1], (const Variant **)argptrs.ptr(), argptrs.size());
+ live_editor->_node_call_func(p_args[0], p_args[1], argptrs.size() ? (const Variant **)argptrs.ptr() : nullptr, argptrs.size());
} else if (p_msg == "live_res_call") {
- ERR_FAIL_COND_V(p_args.size() < 10, ERR_INVALID_DATA);
-
+ ERR_FAIL_COND_V(p_args.size() < 2, ERR_INVALID_DATA);
LocalVector<Variant> args;
LocalVector<Variant *> argptrs;
args.resize(p_args.size() - 2);
@@ -184,7 +184,7 @@ Error SceneDebugger::parse_message(void *p_user, const String &p_msg, const Arra
args[i] = p_args[i + 2];
argptrs[i] = &args[i];
}
- live_editor->_res_call_func(p_args[0], p_args[1], (const Variant **)argptrs.ptr(), argptrs.size());
+ live_editor->_res_call_func(p_args[0], p_args[1], argptrs.size() ? (const Variant **)argptrs.ptr() : nullptr, argptrs.size());
} else if (p_msg == "live_create_node") {
ERR_FAIL_COND_V(p_args.size() < 3, ERR_INVALID_DATA);
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/code_edit.cpp b/scene/gui/code_edit.cpp
index 1944b8db98..eee59606e3 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -2258,9 +2258,8 @@ bool CodeEdit::is_symbol_lookup_on_click_enabled() const {
return symbol_lookup_on_click_enabled;
}
-String CodeEdit::get_text_for_symbol_lookup() {
+String CodeEdit::get_text_for_symbol_lookup() const {
Point2i mp = get_local_mouse_pos();
-
Point2i pos = get_line_column_at_pos(mp, false);
int line = pos.y;
int col = pos.x;
@@ -2269,25 +2268,29 @@ String CodeEdit::get_text_for_symbol_lookup() {
return String();
}
- StringBuilder lookup_text;
+ return get_text_with_cursor_char(line, col);
+}
+
+String CodeEdit::get_text_with_cursor_char(int p_line, int p_column) const {
const int text_size = get_line_count();
+ StringBuilder result;
for (int i = 0; i < text_size; i++) {
String line_text = get_line(i);
-
- if (i == line) {
- lookup_text += line_text.substr(0, col);
+ if (i == p_line && p_column >= 0 && p_column <= line_text.size()) {
+ result += line_text.substr(0, p_column);
/* Not unicode, represents the cursor. */
- lookup_text += String::chr(0xFFFF);
- lookup_text += line_text.substr(col, line_text.size());
+ result += String::chr(0xFFFF);
+ result += line_text.substr(p_column, line_text.size());
} else {
- lookup_text += line_text;
+ result += line_text;
}
if (i != text_size - 1) {
- lookup_text += "\n";
+ result += "\n";
}
}
- return lookup_text.as_string();
+
+ return result.as_string();
}
void CodeEdit::set_symbol_lookup_word_as_valid(bool p_valid) {
@@ -2472,6 +2475,7 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_symbol_lookup_on_click_enabled"), &CodeEdit::is_symbol_lookup_on_click_enabled);
ClassDB::bind_method(D_METHOD("get_text_for_symbol_lookup"), &CodeEdit::get_text_for_symbol_lookup);
+ ClassDB::bind_method(D_METHOD("get_text_with_cursor_char", "line", "column"), &CodeEdit::get_text_with_cursor_char);
ClassDB::bind_method(D_METHOD("set_symbol_lookup_word_as_valid", "valid"), &CodeEdit::set_symbol_lookup_word_as_valid);
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index 6933eb9392..a3c968da60 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -455,7 +455,8 @@ public:
void set_symbol_lookup_on_click_enabled(bool p_enabled);
bool is_symbol_lookup_on_click_enabled() const;
- String get_text_for_symbol_lookup();
+ String get_text_for_symbol_lookup() const;
+ String get_text_with_cursor_char(int p_line, int p_column) const;
void set_symbol_lookup_word_as_valid(bool p_valid);
diff --git a/scene/gui/color_mode.cpp b/scene/gui/color_mode.cpp
index 38e6048b91..adea06eee7 100644
--- a/scene/gui/color_mode.cpp
+++ b/scene/gui/color_mode.cpp
@@ -105,6 +105,17 @@ void ColorModeRGB::slider_draw(int p_which) {
slider->draw_polygon(pos, col);
}
+void ColorModeHSV::_value_changed() {
+ Vector<float> values = color_picker->get_active_slider_values();
+
+ if (values[1] > 0 || values[0] != cached_hue) {
+ cached_hue = values[0];
+ }
+ if (values[2] > 0 || values[1] != cached_saturation) {
+ cached_saturation = values[1];
+ }
+}
+
String ColorModeHSV::get_slider_label(int idx) const {
ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label.");
return labels[idx];
@@ -121,14 +132,14 @@ float ColorModeHSV::get_slider_value(int idx) const {
if (color_picker->get_pick_color().get_s() > 0) {
return color_picker->get_pick_color().get_h() * 360.0;
} else {
- return color_picker->get_cached_hue();
+ return cached_hue;
}
}
case 1: {
if (color_picker->get_pick_color().get_v() > 0) {
return color_picker->get_pick_color().get_s() * 100.0;
} else {
- return color_picker->get_cached_saturation();
+ return cached_saturation;
}
}
case 2:
@@ -167,16 +178,16 @@ void ColorModeHSV::slider_draw(int p_which) {
right_color = color;
right_color.a = 1;
} else if (p_which == 0) {
- Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker"));
- slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false);
- return;
+ float v = color.get_v();
+ left_color = Color(v, v, v);
+ right_color = left_color;
} else {
Color s_col;
Color v_col;
s_col.set_hsv(color.get_h(), 0, color.get_v());
left_color = (p_which == 1) ? s_col : Color(0, 0, 0);
- float s_col_hue = (color.get_s() == 0.0) ? color_picker->get_cached_hue() / 360.0 : color.get_h();
+ float s_col_hue = (Math::is_zero_approx(color.get_s())) ? cached_hue / 360.0 : color.get_h();
s_col.set_hsv(s_col_hue, 1, color.get_v());
v_col.set_hsv(color.get_h(), color.get_s(), 1);
right_color = (p_which == 1) ? s_col : v_col;
@@ -191,6 +202,11 @@ void ColorModeHSV::slider_draw(int p_which) {
pos.set(3, Vector2(0, margin));
slider->draw_polygon(pos, col);
+
+ if (p_which == 0) { // H
+ Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker"));
+ slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false, Color::from_hsv(0, 0, color.get_v(), color.get_s()));
+ }
}
String ColorModeRAW::get_slider_label(int idx) const {
@@ -262,6 +278,17 @@ bool ColorModeRAW::apply_theme() const {
return true;
}
+void ColorModeOKHSL::_value_changed() {
+ Vector<float> values = color_picker->get_active_slider_values();
+
+ if (values[1] > 0 || values[0] != cached_hue) {
+ cached_hue = values[0];
+ }
+ if (values[2] > 0 || values[1] != cached_saturation) {
+ cached_saturation = values[1];
+ }
+}
+
String ColorModeOKHSL::get_slider_label(int idx) const {
ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label.");
return labels[idx];
@@ -274,10 +301,20 @@ float ColorModeOKHSL::get_slider_max(int idx) const {
float ColorModeOKHSL::get_slider_value(int idx) const {
switch (idx) {
- case 0:
- return color_picker->get_pick_color().get_ok_hsl_h() * 360.0;
- case 1:
- return color_picker->get_pick_color().get_ok_hsl_s() * 100.0;
+ case 0: {
+ if (color_picker->get_pick_color().get_ok_hsl_s() > 0) {
+ return color_picker->get_pick_color().get_ok_hsl_h() * 360.0;
+ } else {
+ return cached_hue;
+ }
+ }
+ case 1: {
+ if (color_picker->get_pick_color().get_ok_hsl_l() > 0) {
+ return color_picker->get_pick_color().get_ok_hsl_s() * 100.0;
+ } else {
+ return cached_saturation;
+ }
+ }
case 2:
return color_picker->get_pick_color().get_ok_hsl_l() * 100.0;
case 3:
@@ -299,12 +336,6 @@ void ColorModeOKHSL::slider_draw(int p_which) {
Size2 size = slider->get_size();
const real_t margin = 16 * color_picker->get_theme_default_base_scale();
- if (p_which == 0) { // H
- Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_okhsl_hue"), SNAME("ColorPicker"));
- slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false);
- return;
- }
-
Vector<Vector2> pos;
Vector<Color> col;
Color left_color;
@@ -316,8 +347,11 @@ void ColorModeOKHSL::slider_draw(int p_which) {
col.resize(6);
left_color = Color(0, 0, 0);
Color middle_color;
- middle_color.set_ok_hsl(color.get_ok_hsl_h(), color.get_ok_hsl_s(), 0.5);
- right_color.set_ok_hsl(color.get_ok_hsl_h(), color.get_ok_hsl_s(), 1);
+ float slider_hue = (Math::is_zero_approx(color.get_ok_hsl_s())) ? cached_hue / 360.0 : color.get_ok_hsl_h();
+ float slider_sat = (Math::is_zero_approx(color.get_ok_hsl_l())) ? cached_saturation / 100.0 : color.get_ok_hsl_s();
+
+ middle_color.set_ok_hsl(slider_hue, slider_sat, 0.5);
+ right_color.set_ok_hsl(slider_hue, slider_sat, 1);
col.set(0, left_color);
col.set(1, middle_color);
@@ -331,7 +365,7 @@ void ColorModeOKHSL::slider_draw(int p_which) {
pos.set(3, Vector2(size.x, margin));
pos.set(4, Vector2(size.x * 0.5, margin));
pos.set(5, Vector2(0, margin));
- } else { // A / S
+ } else {
pos.resize(4);
col.resize(4);
@@ -342,9 +376,14 @@ void ColorModeOKHSL::slider_draw(int p_which) {
left_color.a = 0;
right_color = color;
right_color.a = 1;
+ } else if (p_which == 0) {
+ float l = color.get_ok_hsl_l();
+ left_color = Color(l, l, l);
+ right_color = left_color;
} else {
left_color.set_ok_hsl(color.get_ok_hsl_h(), 0, color.get_ok_hsl_l());
- right_color.set_ok_hsl(color.get_ok_hsl_h(), 1, color.get_ok_hsl_l());
+ float s_col_hue = (Math::is_zero_approx(color.get_ok_hsl_s())) ? cached_hue / 360.0 : color.get_ok_hsl_h();
+ right_color.set_ok_hsl(s_col_hue, 1, color.get_ok_hsl_l());
}
col.set(0, left_color);
@@ -358,4 +397,10 @@ void ColorModeOKHSL::slider_draw(int p_which) {
}
slider->draw_polygon(pos, col);
+
+ if (p_which == 0) { // H
+ Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_okhsl_hue"), SNAME("ColorPicker"));
+ slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false, Color::from_hsv(0, 0, color.get_ok_hsl_l() * 2.0, color.get_ok_hsl_s()));
+ return;
+ }
}
diff --git a/scene/gui/color_mode.h b/scene/gui/color_mode.h
index f681df61d2..bbbf3fbaac 100644
--- a/scene/gui/color_mode.h
+++ b/scene/gui/color_mode.h
@@ -49,6 +49,8 @@ public:
virtual Color get_color() const = 0;
+ virtual void _value_changed(){};
+
virtual void slider_draw(int p_which) = 0;
virtual bool apply_theme() const { return false; }
virtual ColorPicker::PickerShapeType get_shape_override() const { return ColorPicker::SHAPE_MAX; }
@@ -61,6 +63,8 @@ class ColorModeHSV : public ColorMode {
public:
String labels[3] = { "H", "S", "V" };
float slider_max[4] = { 359, 100, 100, 255 };
+ float cached_hue = 0.0;
+ float cached_saturation = 0.0;
virtual String get_name() const override { return "HSV"; }
@@ -71,6 +75,8 @@ public:
virtual Color get_color() const override;
+ virtual void _value_changed() override;
+
virtual void slider_draw(int p_which) override;
ColorModeHSV(ColorPicker *p_color_picker) :
@@ -121,6 +127,8 @@ class ColorModeOKHSL : public ColorMode {
public:
String labels[3] = { "H", "S", "L" };
float slider_max[4] = { 359, 100, 100, 255 };
+ float cached_hue = 0.0;
+ float cached_saturation = 0.0;
virtual String get_name() const override { return "OKHSL"; }
@@ -131,6 +139,8 @@ public:
virtual Color get_color() const override;
+ virtual void _value_changed() override;
+
virtual void slider_draw(int p_which) override;
virtual ColorPicker::PickerShapeType get_shape_override() const override { return ColorPicker::SHAPE_OKHSL_CIRCLE; }
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 604f09a3d9..2a0f85a1be 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"
@@ -375,15 +378,7 @@ void ColorPicker::_value_changed(double) {
}
color = modes[current_mode]->get_color();
-
- if (current_mode == MODE_HSV) {
- if (sliders[1]->get_value() > 0 || sliders[0]->get_value() != cached_hue) {
- cached_hue = sliders[0]->get_value();
- }
- if (sliders[2]->get_value() > 0 || sliders[1]->get_value() != cached_saturation) {
- cached_saturation = sliders[1]->get_value();
- }
- }
+ modes[current_mode]->_value_changed();
if (current_mode == MODE_HSV || current_mode == MODE_OKHSL) {
h = sliders[0]->get_value() / 360.0;
@@ -564,7 +559,7 @@ void ColorPicker::_html_submitted(const String &p_html) {
}
const Color previous_color = color;
- color = Color::from_string(p_html, previous_color);
+ color = Color::from_string(p_html.strip_edges(), previous_color);
if (!is_editing_alpha()) {
color.a = previous_color.a;
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 425213be90..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;
@@ -204,8 +205,6 @@ private:
float h = 0.0;
float s = 0.0;
float v = 0.0;
- float cached_hue = 0.0;
- float cached_saturation = 0.0;
Color last_color;
struct ThemeCache {
@@ -296,9 +295,6 @@ public:
#ifdef TOOLS_ENABLED
void set_editor_settings(Object *p_editor_settings);
#endif
- float get_cached_hue() { return cached_hue; };
- float get_cached_saturation() { return cached_saturation; };
-
HSlider *get_slider(int idx);
Vector<float> get_active_slider_values();
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/dialogs.cpp b/scene/gui/dialogs.cpp
index 0860bf3968..902b76c5a7 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -286,18 +286,29 @@ void AcceptDialog::_custom_action(const String &p_action) {
custom_action(p_action);
}
+void AcceptDialog::_custom_button_visibility_changed(Button *button) {
+ Control *right_spacer = Object::cast_to<Control>(button->get_meta("__right_spacer"));
+ if (right_spacer) {
+ right_spacer->set_visible(button->is_visible());
+ }
+}
+
Button *AcceptDialog::add_button(const String &p_text, bool p_right, const String &p_action) {
Button *button = memnew(Button);
button->set_text(p_text);
+ Control *right_spacer;
if (p_right) {
buttons_hbox->add_child(button);
- buttons_hbox->add_spacer();
+ right_spacer = buttons_hbox->add_spacer();
} else {
buttons_hbox->add_child(button);
buttons_hbox->move_child(button, 0);
- buttons_hbox->add_spacer(true);
+ right_spacer = buttons_hbox->add_spacer(true);
}
+ button->set_meta("__right_spacer", right_spacer);
+
+ button->connect("visibility_changed", callable_mp(this, &AcceptDialog::_custom_button_visibility_changed).bind(button));
child_controls_changed();
if (is_visible()) {
@@ -330,6 +341,12 @@ void AcceptDialog::remove_button(Control *p_button) {
ERR_FAIL_COND_MSG(button->get_parent() != buttons_hbox, vformat("Cannot remove button %s as it does not belong to this dialog.", button->get_name()));
ERR_FAIL_COND_MSG(button == ok_button, "Cannot remove dialog's OK button.");
+ Control *right_spacer = Object::cast_to<Control>(button->get_meta("__right_spacer"));
+ if (right_spacer) {
+ ERR_FAIL_COND_MSG(right_spacer->get_parent() != buttons_hbox, vformat("Cannot remove button %s as its associated spacer does not belong to this dialog.", button->get_name()));
+ }
+
+ button->disconnect("visibility_changed", callable_mp(this, &AcceptDialog::_custom_button_visibility_changed));
if (button->is_connected("pressed", callable_mp(this, &AcceptDialog::_custom_action))) {
button->disconnect("pressed", callable_mp(this, &AcceptDialog::_custom_action));
}
@@ -337,11 +354,10 @@ void AcceptDialog::remove_button(Control *p_button) {
button->disconnect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed));
}
- Node *right_spacer = buttons_hbox->get_child(button->get_index() + 1);
- // Should always be valid but let's avoid crashing.
if (right_spacer) {
buttons_hbox->remove_child(right_spacer);
- memdelete(right_spacer);
+ button->remove_meta("__right_spacer");
+ right_spacer->queue_free();
}
buttons_hbox->remove_child(button);
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index 1821a886c0..109c7172d0 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -60,6 +60,7 @@ class AcceptDialog : public Window {
} theme_cache;
void _custom_action(const String &p_action);
+ void _custom_button_visibility_changed(Button *button);
void _update_child_rects();
static bool swap_cancel_ok;
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..cb9fba44e5 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -35,10 +35,18 @@
#include "core/os/keyboard.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
+#include "scene/gui/graph_edit_arranger.h"
#include "scene/gui/view_panner.h"
+#include "scene/resources/style_box_flat.h"
constexpr int MINIMAP_OFFSET = 12;
constexpr int MINIMAP_PADDING = 5;
+constexpr int MIN_DRAG_DISTANCE_FOR_VALID_CONNECTION = 20;
+constexpr int MAX_CONNECTION_LINE_CURVE_TESSELATION_STAGES = 5;
+constexpr int GRID_MINOR_STEPS_PER_MAJOR_LINE = 10;
+constexpr int GRID_MIN_SNAPPING_DISTANCE = 2;
+constexpr int GRID_MAX_SNAPPING_DISTANCE = 100;
+constexpr float CONNECTING_TARGET_LINE_COLOR_BRIGHTENING = 0.4;
bool GraphEditFilter::has_point(const Point2 &p_point) const {
return ge->_filter_input(p_point);
@@ -75,23 +83,23 @@ void GraphEditMinimap::update_minimap() {
Vector2 graph_offset = _get_graph_offset();
Vector2 graph_size = _get_graph_size();
- camera_position = ge->get_scroll_ofs() - graph_offset;
+ camera_position = ge->get_scroll_offset() - graph_offset;
camera_size = ge->get_size();
Vector2 render_size = _get_render_size();
- float target_ratio = render_size.x / render_size.y;
- float graph_ratio = graph_size.x / graph_size.y;
+ float target_ratio = render_size.width / render_size.height;
+ float graph_ratio = graph_size.width / graph_size.height;
graph_proportions = graph_size;
graph_padding = Vector2(0, 0);
if (graph_ratio > target_ratio) {
- graph_proportions.x = graph_size.x;
- graph_proportions.y = graph_size.x / target_ratio;
- graph_padding.y = Math::abs(graph_size.y - graph_proportions.y) / 2;
+ graph_proportions.width = graph_size.width;
+ graph_proportions.height = graph_size.width / target_ratio;
+ graph_padding.y = Math::abs(graph_size.height - graph_proportions.y) / 2;
} else {
- graph_proportions.x = graph_size.y * target_ratio;
- graph_proportions.y = graph_size.y;
- graph_padding.x = Math::abs(graph_size.x - graph_proportions.x) / 2;
+ graph_proportions.width = graph_size.height * target_ratio;
+ graph_proportions.height = graph_size.height;
+ graph_padding.x = Math::abs(graph_size.width - graph_proportions.x) / 2;
}
// This centers minimap inside the minimap rectangle.
@@ -114,17 +122,17 @@ Vector2 GraphEditMinimap::_get_render_size() {
}
Vector2 GraphEditMinimap::_get_graph_offset() {
- return Vector2(ge->h_scroll->get_min(), ge->v_scroll->get_min());
+ return Vector2(ge->h_scrollbar->get_min(), ge->v_scrollbar->get_min());
}
Vector2 GraphEditMinimap::_get_graph_size() {
- Vector2 graph_size = Vector2(ge->h_scroll->get_max(), ge->v_scroll->get_max()) - Vector2(ge->h_scroll->get_min(), ge->v_scroll->get_min());
+ Vector2 graph_size = Vector2(ge->h_scrollbar->get_max(), ge->v_scrollbar->get_max()) - Vector2(ge->h_scrollbar->get_min(), ge->v_scrollbar->get_min());
- if (graph_size.x == 0) {
- graph_size.x = 1;
+ if (graph_size.width == 0) {
+ graph_size.width = 1;
}
- if (graph_size.y == 0) {
- graph_size.y = 1;
+ if (graph_size.height == 0) {
+ graph_size.height = 1;
}
return graph_size;
@@ -134,8 +142,8 @@ Vector2 GraphEditMinimap::_convert_from_graph_position(const Vector2 &p_position
Vector2 map_position = Vector2(0, 0);
Vector2 render_size = _get_render_size();
- map_position.x = p_position.x * render_size.x / graph_proportions.x;
- map_position.y = p_position.y * render_size.y / graph_proportions.y;
+ map_position.x = p_position.x * render_size.width / graph_proportions.x;
+ map_position.y = p_position.y * render_size.height / graph_proportions.y;
return map_position;
}
@@ -144,8 +152,8 @@ Vector2 GraphEditMinimap::_convert_to_graph_position(const Vector2 &p_position)
Vector2 graph_position = Vector2(0, 0);
Vector2 render_size = _get_render_size();
- graph_position.x = p_position.x * graph_proportions.x / render_size.x;
- graph_position.y = p_position.y * graph_proportions.y / render_size.y;
+ graph_position.x = p_position.x * graph_proportions.x / render_size.width;
+ graph_position.y = p_position.y * graph_proportions.y / render_size.height;
return graph_position;
}
@@ -179,10 +187,10 @@ void GraphEditMinimap::gui_input(const Ref<InputEvent> &p_ev) {
accept_event();
} else if (mm.is_valid() && is_pressing) {
if (is_resizing) {
- // Prevent setting minimap wider than GraphEdit
+ // Prevent setting minimap wider than GraphEdit.
Vector2 new_minimap_size;
- new_minimap_size.x = MIN(get_size().x - mm->get_relative().x, ge->get_size().x - 2.0 * minimap_padding.x);
- new_minimap_size.y = MIN(get_size().y - mm->get_relative().y, ge->get_size().y - 2.0 * minimap_padding.y);
+ new_minimap_size.width = MIN(get_size().width - mm->get_relative().x, ge->get_size().width - 2.0 * minimap_padding.x);
+ new_minimap_size.height = MIN(get_size().height - mm->get_relative().y, ge->get_size().height - 2.0 * minimap_padding.y);
ge->set_minimap_size(new_minimap_size);
queue_redraw();
@@ -196,7 +204,7 @@ void GraphEditMinimap::gui_input(const Ref<InputEvent> &p_ev) {
void GraphEditMinimap::_adjust_graph_scroll(const Vector2 &p_offset) {
Vector2 graph_offset = _get_graph_offset();
- ge->set_scroll_ofs(p_offset + graph_offset - camera_size / 2);
+ ge->set_scroll_offset(p_offset + graph_offset - camera_size / 2);
}
Control::CursorShape GraphEdit::get_cursor_shape(const Point2 &p_pos) const {
@@ -220,9 +228,9 @@ Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const S
return OK;
}
Connection c;
- c.from = p_from;
+ c.from_node = p_from;
c.from_port = p_from_port;
- c.to = p_to;
+ c.to_node = p_to;
c.to_port = p_to_port;
c.activity = 0;
connections.push_back(c);
@@ -236,7 +244,7 @@ Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const S
bool GraphEdit::is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
for (const Connection &E : connections) {
- if (E.from == p_from && E.from_port == p_from_port && E.to == p_to && E.to_port == p_to_port) {
+ if (E.from_node == p_from && E.from_port == p_from_port && E.to_node == p_to && E.to_port == p_to_port) {
return true;
}
}
@@ -246,7 +254,7 @@ bool GraphEdit::is_node_connected(const StringName &p_from, int p_from_port, con
void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
for (const List<Connection>::Element *E = connections.front(); E; E = E->next()) {
- if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) {
+ if (E->get().from_node == p_from && E->get().from_port == p_from_port && E->get().to_node == p_to && E->get().to_port == p_to_port) {
connections.erase(E);
top_layer->queue_redraw();
minimap->queue_redraw();
@@ -261,21 +269,21 @@ void GraphEdit::get_connection_list(List<Connection> *r_connections) const {
*r_connections = connections;
}
-void GraphEdit::set_scroll_ofs(const Vector2 &p_ofs) {
- setting_scroll_ofs = true;
- h_scroll->set_value(p_ofs.x);
- v_scroll->set_value(p_ofs.y);
+void GraphEdit::set_scroll_offset(const Vector2 &p_offset) {
+ setting_scroll_offset = true;
+ h_scrollbar->set_value(p_offset.x);
+ v_scrollbar->set_value(p_offset.y);
_update_scroll();
- setting_scroll_ofs = false;
+ setting_scroll_offset = false;
}
-Vector2 GraphEdit::get_scroll_ofs() const {
- return Vector2(h_scroll->get_value(), v_scroll->get_value());
+Vector2 GraphEdit::get_scroll_offset() const {
+ return Vector2(h_scrollbar->get_value(), v_scrollbar->get_value());
}
void GraphEdit::_scroll_moved(double) {
if (!awaiting_scroll_offset_update) {
- call_deferred(SNAME("_update_scroll_offset"));
+ callable_mp(this, &GraphEdit::_update_scroll_offset).call_deferred();
awaiting_scroll_offset_update = true;
}
top_layer->queue_redraw();
@@ -287,25 +295,26 @@ void GraphEdit::_update_scroll_offset() {
set_block_minimum_size_adjust(true);
for (int i = 0; i < get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (!graph_node) {
continue;
}
- Point2 pos = gn->get_position_offset() * zoom;
- pos -= Point2(h_scroll->get_value(), v_scroll->get_value());
- gn->set_position(pos);
- if (gn->get_scale() != Vector2(zoom, zoom)) {
- gn->set_scale(Vector2(zoom, zoom));
+ Point2 pos = graph_node->get_position_offset() * zoom;
+ pos -= Point2(h_scrollbar->get_value(), v_scrollbar->get_value());
+ graph_node->set_position(pos);
+ if (graph_node->get_scale() != Vector2(zoom, zoom)) {
+ graph_node->set_scale(Vector2(zoom, zoom));
}
}
- connections_layer->set_position(-Point2(h_scroll->get_value(), v_scroll->get_value()));
+ connections_layer->set_position(-Point2(h_scrollbar->get_value(), v_scrollbar->get_value()));
set_block_minimum_size_adjust(false);
awaiting_scroll_offset_update = false;
- if (!setting_scroll_ofs) { //in godot, signals on change value are avoided as a convention
- emit_signal(SNAME("scroll_offset_changed"), get_scroll_ofs());
+ // In Godot, signals on value change are avoided by convention.
+ if (!setting_scroll_offset) {
+ emit_signal(SNAME("scroll_offset_changed"), get_scroll_offset());
}
}
@@ -313,90 +322,93 @@ void GraphEdit::_update_scroll() {
if (updating) {
return;
}
-
updating = true;
set_block_minimum_size_adjust(true);
- Rect2 screen;
+ Rect2 screen_rect;
for (int i = 0; i < get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (!graph_node) {
continue;
}
- Rect2 r;
- r.position = gn->get_position_offset() * zoom;
- r.size = gn->get_size() * zoom;
- screen = screen.merge(r);
+ Rect2 node_rect;
+ node_rect.position = graph_node->get_position_offset() * zoom;
+ node_rect.size = graph_node->get_size() * zoom;
+ screen_rect = screen_rect.merge(node_rect);
}
- screen.position -= get_size();
- screen.size += get_size() * 2.0;
+ screen_rect.position -= get_size();
+ screen_rect.size += get_size() * 2.0;
- h_scroll->set_min(screen.position.x);
- h_scroll->set_max(screen.position.x + screen.size.x);
- h_scroll->set_page(get_size().x);
- if (h_scroll->get_max() - h_scroll->get_min() <= h_scroll->get_page()) {
- h_scroll->hide();
+ h_scrollbar->set_min(screen_rect.position.x);
+ h_scrollbar->set_max(screen_rect.position.x + screen_rect.size.width);
+ h_scrollbar->set_page(get_size().x);
+ if (h_scrollbar->get_max() - h_scrollbar->get_min() <= h_scrollbar->get_page()) {
+ h_scrollbar->hide();
} else {
- h_scroll->show();
+ h_scrollbar->show();
}
- v_scroll->set_min(screen.position.y);
- v_scroll->set_max(screen.position.y + screen.size.y);
- v_scroll->set_page(get_size().y);
+ v_scrollbar->set_min(screen_rect.position.y);
+ v_scrollbar->set_max(screen_rect.position.y + screen_rect.size.height);
+ v_scrollbar->set_page(get_size().height);
- if (v_scroll->get_max() - v_scroll->get_min() <= v_scroll->get_page()) {
- v_scroll->hide();
+ if (v_scrollbar->get_max() - v_scrollbar->get_min() <= v_scrollbar->get_page()) {
+ v_scrollbar->hide();
} else {
- v_scroll->show();
+ v_scrollbar->show();
}
- Size2 hmin = h_scroll->get_combined_minimum_size();
- Size2 vmin = v_scroll->get_combined_minimum_size();
+ Size2 hmin = h_scrollbar->get_combined_minimum_size();
+ Size2 vmin = v_scrollbar->get_combined_minimum_size();
// Avoid scrollbar overlapping.
- h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, v_scroll->is_visible() ? -vmin.width : 0);
- v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, h_scroll->is_visible() ? -hmin.height : 0);
+ h_scrollbar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, v_scrollbar->is_visible() ? -vmin.width : 0);
+ v_scrollbar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, h_scrollbar->is_visible() ? -hmin.height : 0);
set_block_minimum_size_adjust(false);
if (!awaiting_scroll_offset_update) {
- call_deferred(SNAME("_update_scroll_offset"));
+ callable_mp(this, &GraphEdit::_update_scroll_offset).call_deferred();
awaiting_scroll_offset_update = true;
}
updating = false;
}
-void GraphEdit::_graph_node_raised(Node *p_gn) {
- GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
- ERR_FAIL_NULL(gn);
- if (gn->is_comment()) {
- move_child(gn, 0);
- } else {
- gn->move_to_front();
- }
+void GraphEdit::_graph_node_moved_to_front(Node *p_gn) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
+ ERR_FAIL_NULL(graph_node);
+
+ graph_node->move_to_front();
}
void GraphEdit::_graph_node_selected(Node *p_gn) {
- GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
- ERR_FAIL_NULL(gn);
+ GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
+ ERR_FAIL_NULL(graph_node);
- emit_signal(SNAME("node_selected"), gn);
+ emit_signal(SNAME("node_selected"), graph_node);
}
void GraphEdit::_graph_node_deselected(Node *p_gn) {
- GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
- ERR_FAIL_NULL(gn);
+ GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
+ ERR_FAIL_NULL(graph_node);
- emit_signal(SNAME("node_deselected"), gn);
+ emit_signal(SNAME("node_deselected"), graph_node);
+}
+
+void GraphEdit::_graph_node_resized(Vector2 p_new_minsize, Node *p_gn) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
+ ERR_FAIL_NULL(graph_node);
+
+ graph_node->set_custom_minimum_size(p_new_minsize);
}
void GraphEdit::_graph_node_moved(Node *p_gn) {
- GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
- ERR_FAIL_NULL(gn);
+ GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
+ ERR_FAIL_NULL(graph_node);
top_layer->queue_redraw();
minimap->queue_redraw();
queue_redraw();
@@ -404,8 +416,8 @@ void GraphEdit::_graph_node_moved(Node *p_gn) {
}
void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_gn) {
- GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
- ERR_FAIL_NULL(gn);
+ GraphNode *graph_node = Object::cast_to<GraphNode>(p_gn);
+ ERR_FAIL_NULL(graph_node);
top_layer->queue_redraw();
minimap->queue_redraw();
queue_redraw();
@@ -415,20 +427,22 @@ void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_gn) {
void GraphEdit::add_child_notify(Node *p_child) {
Control::add_child_notify(p_child);
- top_layer->call_deferred(SNAME("raise")); // Top layer always on top!
+ // Keep the top layer always on top!
+ callable_mp((CanvasItem *)top_layer, &CanvasItem::move_to_front).call_deferred();
- GraphNode *gn = Object::cast_to<GraphNode>(p_child);
- if (gn) {
- gn->set_scale(Vector2(zoom, zoom));
- gn->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved).bind(gn));
- gn->connect("node_selected", callable_mp(this, &GraphEdit::_graph_node_selected).bind(gn));
- gn->connect("node_deselected", callable_mp(this, &GraphEdit::_graph_node_deselected).bind(gn));
- gn->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated).bind(gn));
- gn->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised).bind(gn));
- gn->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
- gn->connect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
- _graph_node_moved(gn);
- gn->set_mouse_filter(MOUSE_FILTER_PASS);
+ GraphNode *graph_node = Object::cast_to<GraphNode>(p_child);
+ if (graph_node) {
+ graph_node->set_scale(Vector2(zoom, zoom));
+ graph_node->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved).bind(graph_node));
+ graph_node->connect("node_selected", callable_mp(this, &GraphEdit::_graph_node_selected).bind(graph_node));
+ graph_node->connect("node_deselected", callable_mp(this, &GraphEdit::_graph_node_deselected).bind(graph_node));
+ graph_node->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated).bind(graph_node));
+ graph_node->connect("raise_request", callable_mp(this, &GraphEdit::_graph_node_moved_to_front).bind(graph_node));
+ graph_node->connect("resize_request", callable_mp(this, &GraphEdit::_graph_node_resized).bind(graph_node));
+ graph_node->connect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
+ graph_node->connect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
+ _graph_node_moved(graph_node);
+ graph_node->set_mouse_filter(MOUSE_FILTER_PASS);
}
}
@@ -443,99 +457,100 @@ void GraphEdit::remove_child_notify(Node *p_child) {
}
if (top_layer != nullptr && is_inside_tree()) {
- top_layer->call_deferred(SNAME("raise")); // Top layer always on top!
+ // Keep the top layer always on top!
+ callable_mp((CanvasItem *)top_layer, &CanvasItem::move_to_front).call_deferred();
}
- GraphNode *gn = Object::cast_to<GraphNode>(p_child);
- if (gn) {
- gn->disconnect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved));
- gn->disconnect("node_selected", callable_mp(this, &GraphEdit::_graph_node_selected));
- gn->disconnect("node_deselected", callable_mp(this, &GraphEdit::_graph_node_deselected));
- gn->disconnect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated));
- gn->disconnect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised));
+ GraphNode *graph_node = Object::cast_to<GraphNode>(p_child);
+ if (graph_node) {
+ graph_node->disconnect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved));
+ graph_node->disconnect("node_selected", callable_mp(this, &GraphEdit::_graph_node_selected));
+ graph_node->disconnect("node_deselected", callable_mp(this, &GraphEdit::_graph_node_deselected));
+ graph_node->disconnect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated));
+ graph_node->disconnect("raise_request", callable_mp(this, &GraphEdit::_graph_node_moved_to_front));
// In case of the whole GraphEdit being destroyed these references can already be freed.
if (connections_layer != nullptr && connections_layer->is_inside_tree()) {
- gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
+ graph_node->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
}
if (minimap != nullptr && minimap->is_inside_tree()) {
- gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
+ graph_node->disconnect("item_rect_changed", callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
}
}
}
void GraphEdit::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
port_hotzone_inner_extent = get_theme_constant("port_hotzone_inner_extent");
port_hotzone_outer_extent = get_theme_constant("port_hotzone_outer_extent");
- zoom_minus->set_icon(get_theme_icon(SNAME("minus")));
- zoom_reset->set_icon(get_theme_icon(SNAME("reset")));
- zoom_plus->set_icon(get_theme_icon(SNAME("more")));
- snap_button->set_icon(get_theme_icon(SNAME("snap")));
- minimap_button->set_icon(get_theme_icon(SNAME("minimap")));
+ zoom_minus_button->set_icon(get_theme_icon(SNAME("zoom_out")));
+ zoom_reset_button->set_icon(get_theme_icon(SNAME("zoom_reset")));
+ zoom_plus_button->set_icon(get_theme_icon(SNAME("zoom_in")));
+
+ toggle_snapping_button->set_icon(get_theme_icon(SNAME("snapping_toggle")));
+ show_grid_button->set_icon(get_theme_icon(SNAME("grid_toggle")));
+ minimap_button->set_icon(get_theme_icon(SNAME("minimap_toggle")));
layout_button->set_icon(get_theme_icon(SNAME("layout")));
zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale());
} break;
case NOTIFICATION_READY: {
- Size2 hmin = h_scroll->get_combined_minimum_size();
- Size2 vmin = v_scroll->get_combined_minimum_size();
-
- h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0);
- h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height);
- h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
-
- v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width);
- v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
- v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
+ Size2 hmin = h_scrollbar->get_combined_minimum_size();
+ Size2 vmin = v_scrollbar->get_combined_minimum_size();
+
+ h_scrollbar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0);
+ h_scrollbar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
+ h_scrollbar->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height);
+ h_scrollbar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
+
+ v_scrollbar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width);
+ v_scrollbar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
+ v_scrollbar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
+ v_scrollbar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
} break;
case NOTIFICATION_DRAW: {
- draw_style_box(get_theme_stylebox(SNAME("bg")), Rect2(Point2(), get_size()));
+ // Draw background fill.
+ draw_style_box(get_theme_stylebox(SNAME("panel")), Rect2(Point2(), get_size()));
- if (is_using_snap()) {
- // Draw grid.
- int snap = get_snap();
-
- Vector2 offset = get_scroll_ofs() / zoom;
+ // Draw background grid.
+ if (show_grid) {
+ Vector2 offset = get_scroll_offset() / zoom;
Size2 size = get_size() / zoom;
- Point2i from = (offset / float(snap)).floor();
- Point2i len = (size / float(snap)).floor() + Vector2(1, 1);
+ Point2i from_pos = (offset / float(snapping_distance)).floor();
+ Point2i len = (size / float(snapping_distance)).floor() + Vector2(1, 1);
Color grid_minor = get_theme_color(SNAME("grid_minor"));
Color grid_major = get_theme_color(SNAME("grid_major"));
- for (int i = from.x; i < from.x + len.x; i++) {
+ for (int i = from_pos.x; i < from_pos.x + len.x; i++) {
Color color;
- if (ABS(i) % 10 == 0) {
+ if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) {
color = grid_major;
} else {
color = grid_minor;
}
- float base_ofs = i * snap * zoom - offset.x * zoom;
- draw_line(Vector2(base_ofs, 0), Vector2(base_ofs, get_size().height), color);
+ float base_offset = i * snapping_distance * zoom - offset.x * zoom;
+ draw_line(Vector2(base_offset, 0), Vector2(base_offset, get_size().height), color);
}
- for (int i = from.y; i < from.y + len.y; i++) {
+ for (int i = from_pos.y; i < from_pos.y + len.y; i++) {
Color color;
- if (ABS(i) % 10 == 0) {
+ if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) {
color = grid_major;
} else {
color = grid_minor;
}
- float base_ofs = i * snap * zoom - offset.y * zoom;
- draw_line(Vector2(0, base_ofs), Vector2(get_size().width, base_ofs), color);
+ float base_offset = i * snapping_distance * zoom - offset.y * zoom;
+ draw_line(Vector2(0, base_offset), Vector2(get_size().width, base_offset), color);
}
}
} break;
@@ -548,65 +563,38 @@ void GraphEdit::_notification(int p_what) {
}
}
-void GraphEdit::_update_comment_enclosed_nodes_list(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes) {
- Rect2 comment_node_rect = p_node->get_rect();
-
- Vector<GraphNode *> enclosed_nodes;
- for (int i = 0; i < get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn || gn->is_selected()) {
- continue;
- }
-
- Rect2 node_rect = gn->get_rect();
-
- bool included = comment_node_rect.encloses(node_rect);
- if (included) {
- enclosed_nodes.push_back(gn);
- }
- }
-
- p_comment_enclosed_nodes.insert(p_node->get_name(), enclosed_nodes);
-}
-
-void GraphEdit::_set_drag_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, bool p_drag) {
- for (int i = 0; i < p_comment_enclosed_nodes[p_node->get_name()].size(); i++) {
- p_comment_enclosed_nodes[p_node->get_name()][i]->set_drag(p_drag);
- }
-}
-
-void GraphEdit::_set_position_of_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, Vector2 p_drag_accum) {
- for (int i = 0; i < p_comment_enclosed_nodes[p_node->get_name()].size(); i++) {
- Vector2 pos = (p_comment_enclosed_nodes[p_node->get_name()][i]->get_drag_from() * zoom + drag_accum) / zoom;
- if (is_using_snap() ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) {
- const int snap = get_snap();
- pos = pos.snapped(Vector2(snap, snap));
- }
- p_comment_enclosed_nodes[p_node->get_name()][i]->set_position_offset(pos);
- }
-}
-
bool GraphEdit::_filter_input(const Point2 &p_point) {
Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (!graph_node || !graph_node->is_visible_in_tree()) {
continue;
}
- for (int j = 0; j < gn->get_connection_input_count(); j++) {
+ for (int j = 0; j < graph_node->get_connection_input_count(); j++) {
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
- port_size.height = MAX(port_size.height, gn->get_connection_input_height(j));
- if (is_in_input_hotzone(gn, j, p_point / zoom, port_size)) {
+
+ // Determine slot height.
+ int slot_index = graph_node->get_connection_input_slot(j);
+ Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
+
+ port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
+
+ if (is_in_input_hotzone(graph_node, j, p_point / zoom, port_size)) {
return true;
}
}
- for (int j = 0; j < gn->get_connection_output_count(); j++) {
+ for (int j = 0; j < graph_node->get_connection_output_count(); j++) {
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
- port_size.height = MAX(port_size.height, gn->get_connection_output_height(j));
- if (is_in_output_hotzone(gn, j, p_point / zoom, port_size)) {
+
+ // Determine slot height.
+ int slot_index = graph_node->get_connection_output_slot(j);
+ Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
+ port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
+
+ if (is_in_output_hotzone(graph_node, j, p_point / zoom, port_size)) {
return true;
}
}
@@ -623,24 +611,28 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_valid = false;
click_pos = mb->get_position() / zoom;
for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (!graph_node || !graph_node->is_visible_in_tree()) {
continue;
}
- for (int j = 0; j < gn->get_connection_output_count(); j++) {
- Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
+ for (int j = 0; j < graph_node->get_connection_output_count(); j++) {
+ Vector2 pos = graph_node->get_connection_output_position(j) * zoom + graph_node->get_position();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
- port_size.height = MAX(port_size.height, gn->get_connection_output_height(j));
- if (is_in_output_hotzone(gn, j, click_pos, port_size)) {
- if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) {
- //check disconnect
+ // Determine slot height.
+ int slot_index = graph_node->get_connection_output_slot(j);
+ Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
+ port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
+
+ if (is_in_output_hotzone(graph_node, j, click_pos, port_size)) {
+ if (valid_left_disconnect_types.has(graph_node->get_connection_output_type(j))) {
+ // Check disconnect.
for (const Connection &E : connections) {
- if (E.from == gn->get_name() && E.from_port == j) {
- Node *to = get_node(NodePath(E.to));
+ if (E.from_node == graph_node->get_name() && E.from_port == j) {
+ Node *to = get_node(NodePath(E.to_node));
if (Object::cast_to<GraphNode>(to)) {
- connecting_from = E.to;
+ connecting_from = E.to_node;
connecting_index = E.to_port;
connecting_out = false;
connecting_type = Object::cast_to<GraphNode>(to)->get_connection_input_type(E.to_port);
@@ -651,7 +643,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
if (connecting_type >= 0) {
just_disconnected = true;
- emit_signal(SNAME("disconnection_request"), E.from, E.from_port, E.to, E.to_port);
+ emit_signal(SNAME("disconnection_request"), E.from_node, E.from_port, E.to_node, E.to_port);
to = get_node(NodePath(connecting_from)); // Maybe it was erased.
if (Object::cast_to<GraphNode>(to)) {
connecting = true;
@@ -664,11 +656,11 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
}
- connecting_from = gn->get_name();
+ connecting_from = graph_node->get_name();
connecting_index = j;
connecting_out = true;
- connecting_type = gn->get_connection_output_type(j);
- connecting_color = gn->get_connection_output_color(j);
+ connecting_type = graph_node->get_connection_output_type(j);
+ connecting_color = graph_node->get_connection_output_color(j);
connecting_target = false;
connecting_to = pos;
if (connecting_type >= 0) {
@@ -680,20 +672,24 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
}
- for (int j = 0; j < gn->get_connection_input_count(); j++) {
- Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
+ for (int j = 0; j < graph_node->get_connection_input_count(); j++) {
+ Vector2 pos = graph_node->get_connection_input_position(j) + graph_node->get_position();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
- port_size.height = MAX(port_size.height, gn->get_connection_input_height(j));
- if (is_in_input_hotzone(gn, j, click_pos, port_size)) {
- if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) {
+ // Determine slot height.
+ int slot_index = graph_node->get_connection_input_slot(j);
+ Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
+ port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
+
+ if (is_in_input_hotzone(graph_node, j, click_pos, port_size)) {
+ if (right_disconnects || valid_right_disconnect_types.has(graph_node->get_connection_input_type(j))) {
// Check disconnect.
for (const Connection &E : connections) {
- if (E.to == gn->get_name() && E.to_port == j) {
- Node *fr = get_node(NodePath(E.from));
+ if (E.to_node == graph_node->get_name() && E.to_port == j) {
+ Node *fr = get_node(NodePath(E.from_node));
if (Object::cast_to<GraphNode>(fr)) {
- connecting_from = E.from;
+ connecting_from = E.from_node;
connecting_index = E.from_port;
connecting_out = true;
connecting_type = Object::cast_to<GraphNode>(fr)->get_connection_output_type(E.from_port);
@@ -703,8 +699,8 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
just_disconnected = true;
if (connecting_type >= 0) {
- emit_signal(SNAME("disconnection_request"), E.from, E.from_port, E.to, E.to_port);
- fr = get_node(NodePath(connecting_from)); // Maybe it was erased.
+ emit_signal(SNAME("disconnection_request"), E.from_node, E.from_port, E.to_node, E.to_port);
+ fr = get_node(NodePath(connecting_from));
if (Object::cast_to<GraphNode>(fr)) {
connecting = true;
emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, true);
@@ -716,11 +712,11 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
}
- connecting_from = gn->get_name();
+ connecting_from = graph_node->get_name();
connecting_index = j;
connecting_out = false;
- connecting_type = gn->get_connection_input_type(j);
- connecting_color = gn->get_connection_input_color(j);
+ connecting_type = graph_node->get_connection_input_type(j);
+ connecting_color = graph_node->get_connection_input_color(j);
connecting_target = false;
connecting_to = pos;
if (connecting_type >= 0) {
@@ -740,49 +736,61 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_target = false;
top_layer->queue_redraw();
minimap->queue_redraw();
- connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > 20.0;
+
+ connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > MIN_DRAG_DISTANCE_FOR_VALID_CONNECTION;
if (connecting_valid) {
Vector2 mpos = mm->get_position() / zoom;
for (int i = get_child_count() - 1; i >= 0; i--) {
Ref<Texture2D> port_icon = get_theme_icon(SNAME("port"), SNAME("GraphNode"));
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (!graph_node || !graph_node->is_visible_in_tree()) {
continue;
}
if (!connecting_out) {
- for (int j = 0; j < gn->get_connection_output_count(); j++) {
- Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
+ for (int j = 0; j < graph_node->get_connection_output_count(); j++) {
+ Vector2 pos = graph_node->get_connection_output_position(j) + graph_node->get_position();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
- port_size.height = MAX(port_size.height, gn->get_connection_output_height(j));
- int type = gn->get_connection_output_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_output_hotzone(gn, j, mpos, port_size)) {
- if (!is_node_hover_valid(gn->get_name(), j, connecting_from, connecting_index)) {
+ // Determine slot height.
+ int slot_index = graph_node->get_connection_output_slot(j);
+ Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
+ port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
+
+ int type = graph_node->get_connection_output_type(j);
+ if ((type == connecting_type ||
+ valid_connection_types.has(ConnectionType(connecting_type, type))) &&
+ is_in_output_hotzone(graph_node, j, mpos, port_size)) {
+ if (!is_node_hover_valid(graph_node->get_name(), j, connecting_from, connecting_index)) {
continue;
}
connecting_target = true;
connecting_to = pos;
- connecting_target_to = gn->get_name();
+ connecting_target_to = graph_node->get_name();
connecting_target_index = j;
return;
}
}
} else {
- for (int j = 0; j < gn->get_connection_input_count(); j++) {
- Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
+ for (int j = 0; j < graph_node->get_connection_input_count(); j++) {
+ Vector2 pos = graph_node->get_connection_input_position(j) + graph_node->get_position();
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
- port_size.height = MAX(port_size.height, gn->get_connection_input_height(j));
- int type = gn->get_connection_input_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_input_hotzone(gn, j, mpos, port_size)) {
- if (!is_node_hover_valid(connecting_from, connecting_index, gn->get_name(), j)) {
+ // Determine slot height.
+ int slot_index = graph_node->get_connection_input_slot(j);
+ Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index));
+ port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
+
+ int type = graph_node->get_connection_input_type(j);
+ if ((type == connecting_type || valid_connection_types.has(ConnectionType(connecting_type, type))) &&
+ is_in_input_hotzone(graph_node, j, mpos, port_size)) {
+ if (!is_node_hover_valid(connecting_from, connecting_index, graph_node->get_name(), j)) {
continue;
}
connecting_target = true;
connecting_to = pos;
- connecting_target_to = gn->get_name();
+ connecting_target_to = graph_node->get_name();
connecting_target_index = j;
return;
}
@@ -843,30 +851,30 @@ bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &mpos
}
}
-bool GraphEdit::is_in_input_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
+bool GraphEdit::is_in_input_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
bool success;
- if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_node, p_port, p_mouse_pos, success)) {
+ if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_graph_node, p_port, p_mouse_pos, success)) {
return success;
} else {
- Vector2 pos = p_node->get_connection_input_position(p_port) + p_node->get_position();
+ Vector2 pos = p_graph_node->get_connection_input_position(p_port) + p_graph_node->get_position();
return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, true);
}
}
-bool GraphEdit::is_in_output_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
- if (p_node->is_resizable()) {
- Ref<Texture2D> resizer = p_node->get_theme_icon(SNAME("resizer"));
- Rect2 resizer_rect = Rect2(p_node->get_position() / zoom + p_node->get_size() - resizer->get_size(), resizer->get_size());
+bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
+ if (p_graph_node->is_resizable()) {
+ Ref<Texture2D> resizer = p_graph_node->get_theme_icon(SNAME("resizer"));
+ Rect2 resizer_rect = Rect2(p_graph_node->get_position() / zoom + p_graph_node->get_size() - resizer->get_size(), resizer->get_size());
if (resizer_rect.has_point(p_mouse_pos)) {
return false;
}
}
bool success;
- if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_node, p_port, p_mouse_pos, success)) {
+ if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_graph_node, p_port, p_mouse_pos, success)) {
return success;
} else {
- Vector2 pos = p_node->get_connection_output_position(p_port) + p_node->get_position();
+ Vector2 pos = p_graph_node->get_connection_output_position(p_port) + p_graph_node->get_position();
return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, false);
}
}
@@ -925,7 +933,7 @@ PackedVector2Array GraphEdit::get_connection_line(const Vector2 &p_from, const V
curve.set_point_in(1, Vector2(-cp_offset, 0));
if (lines_curvature > 0) {
- return curve.tessellate(5, 2.0);
+ return curve.tessellate(MAX_CONNECTION_LINE_CURVE_TESSELATION_STAGES, 2.0);
} else {
return curve.tessellate(1);
}
@@ -948,31 +956,32 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from
void GraphEdit::_connections_layer_draw() {
Color activity_color = get_theme_color(SNAME("activity"));
+
// Draw connections.
List<List<Connection>::Element *> to_erase;
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
const Connection &c = E->get();
- Node *from = get_node(NodePath(c.from));
- GraphNode *gfrom = Object::cast_to<GraphNode>(from);
+ Node *from = get_node(NodePath(c.from_node));
+ GraphNode *gnode_from = Object::cast_to<GraphNode>(from);
- if (!gfrom) {
+ if (!gnode_from) {
to_erase.push_back(E);
continue;
}
- Node *to = get_node(NodePath(c.to));
- GraphNode *gto = Object::cast_to<GraphNode>(to);
+ Node *to = get_node(NodePath(c.to_node));
+ GraphNode *gnode_to = Object::cast_to<GraphNode>(to);
- if (!gto) {
+ if (!gnode_to) {
to_erase.push_back(E);
continue;
}
- Vector2 frompos = gfrom->get_connection_output_position(c.from_port) + gfrom->get_position_offset() * zoom;
- Color color = gfrom->get_connection_output_color(c.from_port);
- Vector2 topos = gto->get_connection_input_position(c.to_port) + gto->get_position_offset() * zoom;
- Color tocolor = gto->get_connection_input_color(c.to_port);
+ Vector2 frompos = gnode_from->get_connection_output_position(c.from_port) + gnode_from->get_position_offset() * zoom;
+ Color color = gnode_from->get_connection_output_color(c.from_port);
+ Vector2 topos = gnode_to->get_connection_input_position(c.to_port) + gnode_to->get_position_offset() * zoom;
+ Color tocolor = gnode_to->get_connection_input_color(c.to_port);
if (c.activity > 0) {
color = color.lerp(activity_color, c.activity);
@@ -990,33 +999,32 @@ void GraphEdit::_top_layer_draw() {
_update_scroll();
if (connecting) {
- Node *fromn = get_node_or_null(NodePath(connecting_from));
- ERR_FAIL_NULL(fromn);
- GraphNode *from = Object::cast_to<GraphNode>(fromn);
- ERR_FAIL_NULL(from);
+ Node *node_from = get_node_or_null(NodePath(connecting_from));
+ ERR_FAIL_NULL(node_from);
+ GraphNode *graph_node_from = Object::cast_to<GraphNode>(node_from);
+ ERR_FAIL_NULL(graph_node_from);
Vector2 pos;
if (connecting_out) {
- pos = from->get_connection_output_position(connecting_index);
+ pos = graph_node_from->get_connection_output_position(connecting_index);
} else {
- pos = from->get_connection_input_position(connecting_index);
+ pos = graph_node_from->get_connection_input_position(connecting_index);
}
- pos += from->get_position();
+ pos += graph_node_from->get_position();
- Vector2 topos;
- topos = connecting_to;
-
- Color col = connecting_color;
+ Vector2 to_pos = connecting_to;
+ Color line_color = connecting_color;
+ // Draw the line to the mouse cursor brighter when it's over a valid target port.
if (connecting_target) {
- col.r += 0.4;
- col.g += 0.4;
- col.b += 0.4;
+ line_color.r += CONNECTING_TARGET_LINE_COLOR_BRIGHTENING;
+ line_color.g += CONNECTING_TARGET_LINE_COLOR_BRIGHTENING;
+ line_color.b += CONNECTING_TARGET_LINE_COLOR_BRIGHTENING;
}
if (!connecting_out) {
- SWAP(pos, topos);
+ SWAP(pos, to_pos);
}
- _draw_connection_line(top_layer, pos, topos, col, col, lines_thickness, zoom);
+ _draw_connection_line(top_layer, pos, to_pos, line_color, line_color, lines_thickness, zoom);
}
if (box_selecting) {
@@ -1034,51 +1042,28 @@ void GraphEdit::_minimap_draw() {
// Draw the minimap background.
Rect2 minimap_rect = Rect2(Point2(), minimap->get_size());
- minimap->draw_style_box(minimap->get_theme_stylebox(SNAME("bg")), minimap_rect);
+ minimap->draw_style_box(minimap->get_theme_stylebox(SNAME("panel")), minimap_rect);
Vector2 graph_offset = minimap->_get_graph_offset();
Vector2 minimap_offset = minimap->minimap_offset;
- // Draw comment graph nodes.
+ // Draw graph nodes.
for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn || !gn->is_comment()) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (!graph_node || !graph_node->is_visible()) {
continue;
}
- Vector2 node_position = minimap->_convert_from_graph_position(gn->get_position_offset() * zoom - graph_offset) + minimap_offset;
- Vector2 node_size = minimap->_convert_from_graph_position(gn->get_size() * zoom);
+ Vector2 node_position = minimap->_convert_from_graph_position(graph_node->get_position_offset() * zoom - graph_offset) + minimap_offset;
+ Vector2 node_size = minimap->_convert_from_graph_position(graph_node->get_size() * zoom);
Rect2 node_rect = Rect2(node_position, node_size);
Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox(SNAME("node"))->duplicate();
// Override default values with colors provided by the GraphNode's stylebox, if possible.
- Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "comment_focus" : "comment");
- if (sbf.is_valid()) {
- Color node_color = sbf->get_bg_color();
- sb_minimap->set_bg_color(node_color);
- }
-
- minimap->draw_style_box(sb_minimap, node_rect);
- }
-
- // Draw regular graph nodes.
- for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn || gn->is_comment()) {
- continue;
- }
-
- Vector2 node_position = minimap->_convert_from_graph_position(gn->get_position_offset() * zoom - graph_offset) + minimap_offset;
- Vector2 node_size = minimap->_convert_from_graph_position(gn->get_size() * zoom);
- Rect2 node_rect = Rect2(node_position, node_size);
-
- Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox(SNAME("node"))->duplicate();
-
- // Override default values with colors provided by the GraphNode's stylebox, if possible.
- Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "selected_frame" : "frame");
- if (sbf.is_valid()) {
- Color node_color = sbf->get_border_color();
+ Ref<StyleBoxFlat> sb_frame = graph_node->get_theme_stylebox(graph_node->is_selected() ? "selected_frame" : "frame");
+ if (sb_frame.is_valid()) {
+ Color node_color = sb_frame->get_bg_color();
sb_minimap->set_bg_color(node_color);
}
@@ -1088,24 +1073,24 @@ void GraphEdit::_minimap_draw() {
// Draw node connections.
Color activity_color = get_theme_color(SNAME("activity"));
for (const Connection &E : connections) {
- Node *from = get_node(NodePath(E.from));
- GraphNode *gfrom = Object::cast_to<GraphNode>(from);
- if (!gfrom) {
+ Node *from = get_node(NodePath(E.from_node));
+ GraphNode *graph_node_from = Object::cast_to<GraphNode>(from);
+ if (!graph_node_from) {
continue;
}
- Node *to = get_node(NodePath(E.to));
- GraphNode *gto = Object::cast_to<GraphNode>(to);
- if (!gto) {
+ Node *node_to = get_node(NodePath(E.to_node));
+ GraphNode *graph_node_to = Object::cast_to<GraphNode>(node_to);
+ if (!graph_node_to) {
continue;
}
- Vector2 from_port_position = gfrom->get_position_offset() * zoom + gfrom->get_connection_output_position(E.from_port);
+ Vector2 from_port_position = graph_node_from->get_position_offset() + graph_node_from->get_connection_output_position(E.from_port);
Vector2 from_position = minimap->_convert_from_graph_position(from_port_position - graph_offset) + minimap_offset;
- Color from_color = gfrom->get_connection_output_color(E.from_port);
- Vector2 to_port_position = gto->get_position_offset() * zoom + gto->get_connection_input_position(E.to_port);
+ Color from_color = graph_node_from->get_connection_output_color(E.from_port);
+ Vector2 to_port_position = graph_node_to->get_position_offset() + graph_node_to->get_connection_input_position(E.to_port);
Vector2 to_position = minimap->_convert_from_graph_position(to_port_position - graph_offset) + minimap_offset;
- Color to_color = gto->get_connection_input_color(E.to_port);
+ Color to_color = graph_node_to->get_connection_input_color(E.to_port);
if (E.activity > 0) {
from_color = from_color.lerp(activity_color, E.activity);
@@ -1126,12 +1111,12 @@ void GraphEdit::_minimap_draw() {
void GraphEdit::set_selected(Node *p_child) {
for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (!graph_node) {
continue;
}
- gn->set_selected(gn == p_child);
+ graph_node->set_selected(graph_node == p_child);
}
}
@@ -1152,21 +1137,17 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
just_selected = true;
drag_accum += mm->get_relative();
for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (gn && gn->is_selected() && gn->is_draggable()) {
- Vector2 pos = (gn->get_drag_from() * zoom + drag_accum) / zoom;
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (graph_node && graph_node->is_selected() && graph_node->is_draggable()) {
+ Vector2 pos = (graph_node->get_drag_from() * zoom + drag_accum) / zoom;
// Snapping can be toggled temporarily by holding down Ctrl.
// This is done here as to not toggle the grid when holding down Ctrl.
- if (is_using_snap() ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) {
- const int snap = get_snap();
- pos = pos.snapped(Vector2(snap, snap));
+ if (snapping_enabled ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ pos = pos.snapped(Vector2(snapping_distance, snapping_distance));
}
- gn->set_position_offset(pos);
- if (gn->is_comment()) {
- _set_position_of_comment_enclosed_nodes(gn, comment_enclosed_nodes, drag_accum);
- }
+ graph_node->set_position_offset(pos);
}
}
}
@@ -1177,18 +1158,18 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
box_selecting_rect = Rect2(box_selecting_from.min(box_selecting_to), (box_selecting_from - box_selecting_to).abs());
for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (!graph_node) {
continue;
}
- Rect2 r = gn->get_rect();
+ Rect2 r = graph_node->get_rect();
bool in_box = r.intersects(box_selecting_rect);
if (in_box) {
- gn->set_selected(box_selection_mode_additive);
+ graph_node->set_selected(box_selection_mode_additive);
} else {
- gn->set_selected(previous_selected.find(gn) != nullptr);
+ graph_node->set_selected(prev_selected.find(graph_node) != nullptr);
}
}
@@ -1202,12 +1183,12 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (box_selecting) {
box_selecting = false;
for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (!graph_node) {
continue;
}
- gn->set_selected(previous_selected.find(gn) != nullptr);
+ graph_node->set_selected(prev_selected.find(graph_node) != nullptr);
}
top_layer->queue_redraw();
minimap->queue_redraw();
@@ -1222,14 +1203,14 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed() && dragging) {
if (!just_selected && drag_accum == Vector2() && Input::get_singleton()->is_key_pressed(Key::CTRL)) {
- //deselect current node
+ // Deselect current node.
for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
- if (gn) {
- Rect2 r = gn->get_rect();
+ if (graph_node) {
+ Rect2 r = graph_node->get_rect();
if (r.has_point(mb->get_position())) {
- gn->set_selected(false);
+ graph_node->set_selected(false);
}
}
}
@@ -1237,12 +1218,9 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (drag_accum != Vector2()) {
for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (gn && gn->is_selected()) {
- gn->set_drag(false);
- if (gn->is_comment()) {
- _set_drag_comment_enclosed_nodes(gn, comment_enclosed_nodes, false);
- }
+ GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
+ if (graph_node && graph_node->is_selected()) {
+ graph_node->set_drag(false);
}
}
}
@@ -1260,28 +1238,29 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
connections_layer->queue_redraw();
}
+ // Node selection logic.
if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
- GraphNode *gn = nullptr;
+ GraphNode *graph_node = nullptr;
// Find node which was clicked on.
for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn_selected = Object::cast_to<GraphNode>(get_child(i));
+ GraphNode *selected_gcontrol = Object::cast_to<GraphNode>(get_child(i));
- if (!gn_selected) {
+ if (!selected_gcontrol) {
continue;
}
- if (gn_selected->is_resizing()) {
+ if (selected_gcontrol->is_resizing()) {
continue;
}
- if (gn_selected->has_point((mb->get_position() - gn_selected->get_position()) / zoom)) {
- gn = gn_selected;
+ if (selected_gcontrol->has_point((mb->get_position() - selected_gcontrol->get_position()) / zoom)) {
+ graph_node = selected_gcontrol;
break;
}
}
- if (gn) {
+ if (graph_node) {
if (_filter_input(mb->get_position())) {
return;
}
@@ -1289,19 +1268,19 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
// Left-clicked on a node, select it.
dragging = true;
drag_accum = Vector2();
- just_selected = !gn->is_selected();
- if (!gn->is_selected() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ just_selected = !graph_node->is_selected();
+ if (!graph_node->is_selected() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
for (int i = 0; i < get_child_count(); i++) {
GraphNode *o_gn = Object::cast_to<GraphNode>(get_child(i));
if (!o_gn) {
continue;
}
- o_gn->set_selected(o_gn == gn);
+ o_gn->set_selected(o_gn == graph_node);
}
}
- gn->set_selected(true);
+ graph_node->set_selected(true);
for (int i = 0; i < get_child_count(); i++) {
GraphNode *o_gn = Object::cast_to<GraphNode>(get_child(i));
if (!o_gn) {
@@ -1309,10 +1288,6 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
if (o_gn->is_selected()) {
o_gn->set_drag(true);
- if (o_gn->is_comment()) {
- _update_comment_enclosed_nodes_list(o_gn, comment_enclosed_nodes);
- _set_drag_comment_enclosed_nodes(o_gn, comment_enclosed_nodes, true);
- }
}
}
@@ -1329,29 +1304,29 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
box_selecting_from = mb->get_position();
if (mb->is_ctrl_pressed()) {
box_selection_mode_additive = true;
- previous_selected.clear();
+ prev_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
if (!gn2 || !gn2->is_selected()) {
continue;
}
- previous_selected.push_back(gn2);
+ prev_selected.push_back(gn2);
}
} else if (mb->is_shift_pressed()) {
box_selection_mode_additive = false;
- previous_selected.clear();
+ prev_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
if (!gn2 || !gn2->is_selected()) {
continue;
}
- previous_selected.push_back(gn2);
+ prev_selected.push_back(gn2);
}
} else {
box_selection_mode_additive = true;
- previous_selected.clear();
+ prev_selected.clear();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
if (!gn2) {
@@ -1368,7 +1343,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
// Box selection ended. Nodes were selected during mouse movement.
box_selecting = false;
box_selecting_rect = Rect2();
- previous_selected.clear();
+ prev_selected.clear();
top_layer->queue_redraw();
minimap->queue_redraw();
}
@@ -1392,7 +1367,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (!gn) {
continue;
}
- if (gn->is_selected() && gn->is_close_button_visible()) {
+ if (gn->is_selected()) {
nodes.push_back(gn->get_name());
}
}
@@ -1404,8 +1379,8 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
void GraphEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {
- h_scroll->set_value(h_scroll->get_value() - p_scroll_vec.x);
- v_scroll->set_value(v_scroll->get_value() - p_scroll_vec.y);
+ h_scrollbar->set_value(h_scrollbar->get_value() - p_scroll_vec.x);
+ v_scrollbar->set_value(v_scrollbar->get_value() - p_scroll_vec.y);
}
void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {
@@ -1414,9 +1389,9 @@ void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputE
void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) {
for (Connection &E : connections) {
- if (E.from == p_from && E.from_port == p_from_port && E.to == p_to && E.to_port == p_to_port) {
+ if (E.from_node == p_from && E.from_port == p_from_port && E.to_node == p_to && E.to_port == p_to_port) {
if (Math::is_equal_approx(E.activity, p_activity)) {
- //update only if changed
+ // Update only if changed.
top_layer->queue_redraw();
minimap->queue_redraw();
connections_layer->queue_redraw();
@@ -1470,22 +1445,22 @@ void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) {
return;
}
- Vector2 sbofs = (Vector2(h_scroll->get_value(), v_scroll->get_value()) + p_center) / zoom;
+ Vector2 scrollbar_offset = (Vector2(h_scrollbar->get_value(), v_scrollbar->get_value()) + p_center) / zoom;
zoom = p_zoom;
top_layer->queue_redraw();
- zoom_minus->set_disabled(zoom == zoom_min);
- zoom_plus->set_disabled(zoom == zoom_max);
+ zoom_minus_button->set_disabled(zoom == zoom_min);
+ zoom_plus_button->set_disabled(zoom == zoom_max);
_update_scroll();
minimap->queue_redraw();
connections_layer->queue_redraw();
if (is_visible_in_tree()) {
- Vector2 ofs = sbofs * zoom - p_center;
- h_scroll->set_value(ofs.x);
- v_scroll->set_value(ofs.y);
+ Vector2 offset = scrollbar_offset * zoom - p_center;
+ h_scrollbar->set_value(offset.x);
+ v_scrollbar->set_value(offset.y);
}
_update_zoom_label();
@@ -1583,9 +1558,9 @@ TypedArray<Dictionary> GraphEdit::_get_connection_list() const {
TypedArray<Dictionary> arr;
for (const Connection &E : conns) {
Dictionary d;
- d["from"] = E.from;
+ d["from_node"] = E.from_node;
d["from_port"] = E.from_port;
- d["to"] = E.to;
+ d["to_node"] = E.to_node;
d["to_port"] = E.to_port;
arr.push_back(d);
}
@@ -1611,47 +1586,71 @@ void GraphEdit::_update_zoom_label() {
}
void GraphEdit::add_valid_connection_type(int p_type, int p_with_type) {
- ConnType ct(p_type, p_with_type);
+ ConnectionType ct(p_type, p_with_type);
valid_connection_types.insert(ct);
}
void GraphEdit::remove_valid_connection_type(int p_type, int p_with_type) {
- ConnType ct(p_type, p_with_type);
+ ConnectionType ct(p_type, p_with_type);
valid_connection_types.erase(ct);
}
bool GraphEdit::is_valid_connection_type(int p_type, int p_with_type) const {
- ConnType ct(p_type, p_with_type);
+ ConnectionType ct(p_type, p_with_type);
return valid_connection_types.has(ct);
}
-void GraphEdit::set_use_snap(bool p_enable) {
- if (snap_button->is_pressed() == p_enable) {
+void GraphEdit::set_snapping_enabled(bool p_enable) {
+ if (snapping_enabled == p_enable) {
return;
}
- snap_button->set_pressed(p_enable);
+
+ snapping_enabled = p_enable;
+ toggle_snapping_button->set_pressed(p_enable);
queue_redraw();
}
-bool GraphEdit::is_using_snap() const {
- return snap_button->is_pressed();
+bool GraphEdit::is_snapping_enabled() const {
+ return snapping_enabled;
+}
+
+void GraphEdit::set_snapping_distance(int p_snapping_distance) {
+ ERR_FAIL_COND_MSG(p_snapping_distance < GRID_MIN_SNAPPING_DISTANCE || p_snapping_distance > GRID_MAX_SNAPPING_DISTANCE,
+ vformat("GraphEdit's snapping distance must be between %d and %d (inclusive)", GRID_MIN_SNAPPING_DISTANCE, GRID_MAX_SNAPPING_DISTANCE));
+ snapping_distance = p_snapping_distance;
+ snapping_distance_spinbox->set_value(p_snapping_distance);
+ queue_redraw();
}
-int GraphEdit::get_snap() const {
- return snap_amount->get_value();
+int GraphEdit::get_snapping_distance() const {
+ return snapping_distance;
}
-void GraphEdit::set_snap(int p_snap) {
- ERR_FAIL_COND(p_snap < 5);
- snap_amount->set_value(p_snap);
+void GraphEdit::set_show_grid(bool p_show) {
+ if (show_grid == p_show) {
+ return;
+ }
+
+ show_grid = p_show;
+ show_grid_button->set_pressed(p_show);
queue_redraw();
}
-void GraphEdit::_snap_toggled() {
+bool GraphEdit::is_showing_grid() const {
+ return show_grid;
+}
+
+void GraphEdit::_snapping_toggled() {
+ snapping_enabled = toggle_snapping_button->is_pressed();
+}
+
+void GraphEdit::_snapping_distance_changed(double) {
+ snapping_distance = snapping_distance_spinbox->get_value();
queue_redraw();
}
-void GraphEdit::_snap_value_changed(double) {
+void GraphEdit::_show_grid_toggled() {
+ show_grid = show_grid_button->is_pressed();
queue_redraw();
}
@@ -1660,8 +1659,8 @@ void GraphEdit::set_minimap_size(Vector2 p_size) {
Vector2 minimap_size = minimap->get_size(); // The size might've been adjusted by the minimum size.
minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
- minimap->set_offset(Side::SIDE_LEFT, -minimap_size.x - MINIMAP_OFFSET);
- minimap->set_offset(Side::SIDE_TOP, -minimap_size.y - MINIMAP_OFFSET);
+ minimap->set_offset(Side::SIDE_LEFT, -minimap_size.width - MINIMAP_OFFSET);
+ minimap->set_offset(Side::SIDE_TOP, -minimap_size.height - MINIMAP_OFFSET);
minimap->set_offset(Side::SIDE_RIGHT, -MINIMAP_OFFSET);
minimap->set_offset(Side::SIDE_BOTTOM, -MINIMAP_OFFSET);
minimap->queue_redraw();
@@ -1752,8 +1751,8 @@ bool GraphEdit::is_connection_lines_antialiased() const {
return lines_antialiased;
}
-HBoxContainer *GraphEdit::get_zoom_hbox() {
- return zoom_hb;
+HBoxContainer *GraphEdit::get_menu_hbox() {
+ return menu_hbox;
}
Ref<ViewPanner> GraphEdit::get_panner() {
@@ -1764,526 +1763,8 @@ void GraphEdit::set_warped_panning(bool p_warped) {
warped_panning = p_warped;
}
-int GraphEdit::_set_operations(SET_OPERATIONS p_operation, HashSet<StringName> &r_u, const HashSet<StringName> &r_v) {
- switch (p_operation) {
- case GraphEdit::IS_EQUAL: {
- for (const StringName &E : r_u) {
- if (!r_v.has(E)) {
- return 0;
- }
- }
- return r_u.size() == r_v.size();
- } break;
- case GraphEdit::IS_SUBSET: {
- if (r_u.size() == r_v.size() && !r_u.size()) {
- return 1;
- }
- for (const StringName &E : r_u) {
- if (!r_v.has(E)) {
- return 0;
- }
- }
- return 1;
- } break;
- case GraphEdit::DIFFERENCE: {
- for (HashSet<StringName>::Iterator E = r_u.begin(); E;) {
- HashSet<StringName>::Iterator N = E;
- ++N;
- if (r_v.has(*E)) {
- r_u.remove(E);
- }
- E = N;
- }
- return r_u.size();
- } break;
- case GraphEdit::UNION: {
- for (const StringName &E : r_v) {
- if (!r_u.has(E)) {
- r_u.insert(E);
- }
- }
- return r_u.size();
- } break;
- default:
- break;
- }
- return -1;
-}
-
-HashMap<int, Vector<StringName>> GraphEdit::_layering(const HashSet<StringName> &r_selected_nodes, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours) {
- HashMap<int, Vector<StringName>> l;
-
- HashSet<StringName> p = r_selected_nodes, q = r_selected_nodes, u, z;
- int current_layer = 0;
- bool selected = false;
-
- while (!_set_operations(GraphEdit::IS_EQUAL, q, u)) {
- _set_operations(GraphEdit::DIFFERENCE, p, u);
- for (const StringName &E : p) {
- HashSet<StringName> n = r_upper_neighbours[E];
- if (_set_operations(GraphEdit::IS_SUBSET, n, z)) {
- Vector<StringName> t;
- t.push_back(E);
- if (!l.has(current_layer)) {
- l.insert(current_layer, Vector<StringName>{});
- }
- selected = true;
- t.append_array(l[current_layer]);
- l.insert(current_layer, t);
- HashSet<StringName> V;
- V.insert(E);
- _set_operations(GraphEdit::UNION, u, V);
- }
- }
- if (!selected) {
- current_layer++;
- uint32_t previous_size_z = z.size();
- _set_operations(GraphEdit::UNION, z, u);
- if (z.size() == previous_size_z) {
- WARN_PRINT("Graph contains cycle(s). The cycle(s) will not be rearranged accurately.");
- Vector<StringName> t;
- if (l.has(0)) {
- t.append_array(l[0]);
- }
- for (const StringName &E : p) {
- t.push_back(E);
- }
- l.insert(0, t);
- break;
- }
- }
- selected = false;
- }
-
- return l;
-}
-
-Vector<StringName> GraphEdit::_split(const Vector<StringName> &r_layer, const HashMap<StringName, Dictionary> &r_crossings) {
- if (!r_layer.size()) {
- return Vector<StringName>();
- }
-
- StringName p = r_layer[Math::random(0, r_layer.size() - 1)];
- Vector<StringName> left;
- Vector<StringName> right;
-
- for (int i = 0; i < r_layer.size(); i++) {
- if (p != r_layer[i]) {
- StringName q = r_layer[i];
- int cross_pq = r_crossings[p][q];
- int cross_qp = r_crossings[q][p];
- if (cross_pq > cross_qp) {
- left.push_back(q);
- } else {
- right.push_back(q);
- }
- }
- }
-
- left.push_back(p);
- left.append_array(right);
- return left;
-}
-
-void GraphEdit::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours, const HashSet<StringName> &r_selected_nodes) {
- for (const StringName &E : r_selected_nodes) {
- r_root[E] = E;
- r_align[E] = E;
- }
-
- if (r_layers.size() == 1) {
- return;
- }
-
- for (unsigned int i = 1; i < r_layers.size(); i++) {
- Vector<StringName> lower_layer = r_layers[i];
- Vector<StringName> upper_layer = r_layers[i - 1];
- int r = -1;
-
- for (int j = 0; j < lower_layer.size(); j++) {
- Vector<Pair<int, StringName>> up;
- StringName current_node = lower_layer[j];
- for (int k = 0; k < upper_layer.size(); k++) {
- StringName adjacent_neighbour = upper_layer[k];
- if (r_upper_neighbours[current_node].has(adjacent_neighbour)) {
- up.push_back(Pair<int, StringName>(k, adjacent_neighbour));
- }
- }
-
- int start = (up.size() - 1) / 2;
- int end = (up.size() - 1) % 2 ? start + 1 : start;
- for (int p = start; p <= end; p++) {
- StringName Align = r_align[current_node];
- if (Align == current_node && r < up[p].first) {
- r_align[up[p].second] = lower_layer[j];
- r_root[current_node] = r_root[up[p].second];
- r_align[current_node] = r_root[up[p].second];
- r = up[p].first;
- }
- }
- }
- }
-}
-
-void GraphEdit::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours) {
- if (r_layers.size() == 1) {
- return;
- }
-
- for (unsigned int i = 1; i < r_layers.size(); i++) {
- Vector<StringName> upper_layer = r_layers[i - 1];
- Vector<StringName> lower_layer = r_layers[i];
- HashMap<StringName, Dictionary> c;
-
- for (int j = 0; j < lower_layer.size(); j++) {
- StringName p = lower_layer[j];
- Dictionary d;
-
- for (int k = 0; k < lower_layer.size(); k++) {
- unsigned int crossings = 0;
- StringName q = lower_layer[k];
-
- if (j != k) {
- for (int h = 1; h < upper_layer.size(); h++) {
- if (r_upper_neighbours[p].has(upper_layer[h])) {
- for (int g = 0; g < h; g++) {
- if (r_upper_neighbours[q].has(upper_layer[g])) {
- crossings++;
- }
- }
- }
- }
- }
- d[q] = crossings;
- }
- c.insert(p, d);
- }
-
- r_layers.insert(i, _split(lower_layer, c));
- }
-}
-
-void GraphEdit::_calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const HashSet<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info) {
- for (const StringName &E : r_block_heads) {
- real_t left = 0;
- StringName u = E;
- StringName v = r_align[u];
- while (u != v && (StringName)r_root[u] != v) {
- String _connection = String(u) + " " + String(v);
- GraphNode *gfrom = Object::cast_to<GraphNode>(r_node_names[u]);
- GraphNode *gto = Object::cast_to<GraphNode>(r_node_names[v]);
-
- Pair<int, int> ports = r_port_info[_connection];
- int pfrom = ports.first;
- int pto = ports.second;
- Vector2 frompos = gfrom->get_connection_output_position(pfrom);
- Vector2 topos = gto->get_connection_input_position(pto);
-
- real_t s = (real_t)r_inner_shifts[u] + (frompos.y - topos.y) / zoom;
- r_inner_shifts[v] = s;
- left = MIN(left, s);
-
- u = v;
- v = (StringName)r_align[v];
- }
-
- u = E;
- do {
- r_inner_shifts[u] = (real_t)r_inner_shifts[u] - left;
- u = (StringName)r_align[u];
- } while (u != E);
- }
-}
-
-float GraphEdit::_calculate_threshold(StringName p_v, StringName p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions) {
-#define MAX_ORDER 2147483647
-#define ORDER(node, layers) \
- for (unsigned int i = 0; i < layers.size(); i++) { \
- int index = layers[i].find(node); \
- if (index > 0) { \
- order = index; \
- break; \
- } \
- order = MAX_ORDER; \
- }
-
- int order = MAX_ORDER;
- float threshold = p_current_threshold;
- if (p_v == p_w) {
- int min_order = MAX_ORDER;
- Connection incoming;
- for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
- if (E->get().to == p_w) {
- ORDER(E->get().from, r_layers);
- if (min_order > order) {
- min_order = order;
- incoming = E->get();
- }
- }
- }
-
- if (incoming.from != StringName()) {
- GraphNode *gfrom = Object::cast_to<GraphNode>(r_node_names[incoming.from]);
- GraphNode *gto = Object::cast_to<GraphNode>(r_node_names[p_w]);
- Vector2 frompos = gfrom->get_connection_output_position(incoming.from_port);
- Vector2 topos = gto->get_connection_input_position(incoming.to_port);
-
- //If connected block node is selected, calculate thershold or add current block to list
- if (gfrom->is_selected()) {
- Vector2 connected_block_pos = r_node_positions[r_root[incoming.from]];
- if (connected_block_pos.y != FLT_MAX) {
- //Connected block is placed. Calculate threshold
- threshold = connected_block_pos.y + (real_t)r_inner_shift[incoming.from] - (real_t)r_inner_shift[p_w] + frompos.y - topos.y;
- }
- }
- }
- }
- if (threshold == FLT_MIN && (StringName)r_align[p_w] == p_v) {
- //This time, pick an outgoing edge and repeat as above!
- int min_order = MAX_ORDER;
- Connection outgoing;
- for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
- if (E->get().from == p_w) {
- ORDER(E->get().to, r_layers);
- if (min_order > order) {
- min_order = order;
- outgoing = E->get();
- }
- }
- }
-
- if (outgoing.to != StringName()) {
- GraphNode *gfrom = Object::cast_to<GraphNode>(r_node_names[p_w]);
- GraphNode *gto = Object::cast_to<GraphNode>(r_node_names[outgoing.to]);
- Vector2 frompos = gfrom->get_connection_output_position(outgoing.from_port);
- Vector2 topos = gto->get_connection_input_position(outgoing.to_port);
-
- //If connected block node is selected, calculate thershold or add current block to list
- if (gto->is_selected()) {
- Vector2 connected_block_pos = r_node_positions[r_root[outgoing.to]];
- if (connected_block_pos.y != FLT_MAX) {
- //Connected block is placed. Calculate threshold
- threshold = connected_block_pos.y + (real_t)r_inner_shift[outgoing.to] - (real_t)r_inner_shift[p_w] + frompos.y - topos.y;
- }
- }
- }
- }
-#undef MAX_ORDER
-#undef ORDER
- return threshold;
-}
-
-void GraphEdit::_place_block(StringName p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions) {
-#define PRED(node, layers) \
- for (unsigned int i = 0; i < layers.size(); i++) { \
- int index = layers[i].find(node); \
- if (index > 0) { \
- predecessor = layers[i][index - 1]; \
- break; \
- } \
- predecessor = StringName(); \
- }
-
- StringName predecessor;
- StringName successor;
- Vector2 pos = r_node_positions[p_v];
-
- if (pos.y == FLT_MAX) {
- pos.y = 0;
- bool initial = false;
- StringName w = p_v;
- real_t threshold = FLT_MIN;
- do {
- PRED(w, r_layers);
- if (predecessor != StringName()) {
- StringName u = r_root[predecessor];
- _place_block(u, p_delta, r_layers, r_root, r_align, r_node_name, r_inner_shift, r_sink, r_shift, r_node_positions);
- threshold = _calculate_threshold(p_v, w, r_node_name, r_layers, r_root, r_align, r_inner_shift, threshold, r_node_positions);
- if ((StringName)r_sink[p_v] == p_v) {
- r_sink[p_v] = r_sink[u];
- }
-
- Vector2 predecessor_root_pos = r_node_positions[u];
- Vector2 predecessor_node_size = Object::cast_to<GraphNode>(r_node_name[predecessor])->get_size();
- if (r_sink[p_v] != r_sink[u]) {
- real_t sc = pos.y + (real_t)r_inner_shift[w] - predecessor_root_pos.y - (real_t)r_inner_shift[predecessor] - predecessor_node_size.y - p_delta;
- r_shift[r_sink[u]] = MIN(sc, (real_t)r_shift[r_sink[u]]);
- } else {
- real_t sb = predecessor_root_pos.y + (real_t)r_inner_shift[predecessor] + predecessor_node_size.y - (real_t)r_inner_shift[w] + p_delta;
- sb = MAX(sb, threshold);
- if (initial) {
- pos.y = sb;
- } else {
- pos.y = MAX(pos.y, sb);
- }
- initial = false;
- }
- }
- threshold = _calculate_threshold(p_v, w, r_node_name, r_layers, r_root, r_align, r_inner_shift, threshold, r_node_positions);
- w = r_align[w];
- } while (w != p_v);
- r_node_positions.insert(p_v, pos);
- }
-
-#undef PRED
-}
-
void GraphEdit::arrange_nodes() {
- if (!arranging_graph) {
- arranging_graph = true;
- } else {
- return;
- }
-
- Dictionary node_names;
- HashSet<StringName> selected_nodes;
-
- bool arrange_entire_graph = true;
- for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
- continue;
- }
-
- node_names[gn->get_name()] = gn;
-
- if (gn->is_selected()) {
- arrange_entire_graph = false;
- }
- }
-
- HashMap<StringName, HashSet<StringName>> upper_neighbours;
- HashMap<StringName, Pair<int, int>> port_info;
- Vector2 origin(FLT_MAX, FLT_MAX);
-
- float gap_v = 100.0f;
- float gap_h = 100.0f;
-
- for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
- continue;
- }
-
- if (gn->is_selected() || arrange_entire_graph) {
- selected_nodes.insert(gn->get_name());
- HashSet<StringName> s;
- for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
- GraphNode *p_from = Object::cast_to<GraphNode>(node_names[E->get().from]);
- if (E->get().to == gn->get_name() && (p_from->is_selected() || arrange_entire_graph) && E->get().to != E->get().from) {
- if (!s.has(p_from->get_name())) {
- s.insert(p_from->get_name());
- }
- String s_connection = String(p_from->get_name()) + " " + String(E->get().to);
- StringName _connection(s_connection);
- Pair<int, int> ports(E->get().from_port, E->get().to_port);
- if (port_info.has(_connection)) {
- Pair<int, int> p_ports = port_info[_connection];
- if (p_ports.first < ports.first) {
- ports = p_ports;
- }
- }
- port_info.insert(_connection, ports);
- }
- }
- upper_neighbours.insert(gn->get_name(), s);
- }
- }
-
- if (!selected_nodes.size()) {
- arranging_graph = false;
- return;
- }
-
- HashMap<int, Vector<StringName>> layers = _layering(selected_nodes, upper_neighbours);
- _crossing_minimisation(layers, upper_neighbours);
-
- Dictionary root, align, sink, shift;
- _horizontal_alignment(root, align, layers, upper_neighbours, selected_nodes);
-
- HashMap<StringName, Vector2> new_positions;
- Vector2 default_position(FLT_MAX, FLT_MAX);
- Dictionary inner_shift;
- HashSet<StringName> block_heads;
-
- for (const StringName &E : selected_nodes) {
- inner_shift[E] = 0.0f;
- sink[E] = E;
- shift[E] = FLT_MAX;
- new_positions.insert(E, default_position);
- if ((StringName)root[E] == E) {
- block_heads.insert(E);
- }
- }
-
- _calculate_inner_shifts(inner_shift, root, node_names, align, block_heads, port_info);
-
- for (const StringName &E : block_heads) {
- _place_block(E, gap_v, layers, root, align, node_names, inner_shift, sink, shift, new_positions);
- }
- origin.y = Object::cast_to<GraphNode>(node_names[layers[0][0]])->get_position_offset().y - (new_positions[layers[0][0]].y + (float)inner_shift[layers[0][0]]);
- origin.x = Object::cast_to<GraphNode>(node_names[layers[0][0]])->get_position_offset().x;
-
- for (const StringName &E : block_heads) {
- StringName u = E;
- float start_from = origin.y + new_positions[E].y;
- do {
- Vector2 cal_pos;
- cal_pos.y = start_from + (real_t)inner_shift[u];
- new_positions.insert(u, cal_pos);
- u = align[u];
- } while (u != E);
- }
-
- // Compute horizontal coordinates individually for layers to get uniform gap.
- float start_from = origin.x;
- float largest_node_size = 0.0f;
-
- for (unsigned int i = 0; i < layers.size(); i++) {
- Vector<StringName> layer = layers[i];
- for (int j = 0; j < layer.size(); j++) {
- float current_node_size = Object::cast_to<GraphNode>(node_names[layer[j]])->get_size().x;
- largest_node_size = MAX(largest_node_size, current_node_size);
- }
-
- for (int j = 0; j < layer.size(); j++) {
- float current_node_size = Object::cast_to<GraphNode>(node_names[layer[j]])->get_size().x;
- Vector2 cal_pos = new_positions[layer[j]];
-
- if (current_node_size == largest_node_size) {
- cal_pos.x = start_from;
- } else {
- float current_node_start_pos = start_from;
- if (current_node_size < largest_node_size / 2) {
- if (!(i || j)) {
- start_from -= (largest_node_size - current_node_size);
- }
- current_node_start_pos = start_from + largest_node_size - current_node_size;
- }
- cal_pos.x = current_node_start_pos;
- }
- new_positions.insert(layer[j], cal_pos);
- }
-
- start_from += largest_node_size + gap_h;
- largest_node_size = 0.0f;
- }
-
- emit_signal(SNAME("begin_node_move"));
- for (const StringName &E : selected_nodes) {
- GraphNode *gn = Object::cast_to<GraphNode>(node_names[E]);
- gn->set_drag(true);
- Vector2 pos = (new_positions[E]);
-
- if (is_using_snap()) {
- const int snap = get_snap();
- pos = pos.snapped(Vector2(snap, snap));
- }
- gn->set_position_offset(pos);
- gn->set_drag(false);
- }
- emit_signal(SNAME("end_node_move"));
- arranging_graph = false;
+ arranger->arrange_nodes();
}
void GraphEdit::_bind_methods() {
@@ -2294,8 +1775,8 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list);
ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections);
ClassDB::bind_method(D_METHOD("force_connection_drag_end"), &GraphEdit::force_connection_drag_end);
- ClassDB::bind_method(D_METHOD("get_scroll_ofs"), &GraphEdit::get_scroll_ofs);
- ClassDB::bind_method(D_METHOD("set_scroll_ofs", "offset"), &GraphEdit::set_scroll_ofs);
+ ClassDB::bind_method(D_METHOD("get_scroll_offset"), &GraphEdit::get_scroll_offset);
+ ClassDB::bind_method(D_METHOD("set_scroll_offset", "offset"), &GraphEdit::set_scroll_offset);
ClassDB::bind_method(D_METHOD("add_valid_right_disconnect_type", "type"), &GraphEdit::add_valid_right_disconnect_type);
ClassDB::bind_method(D_METHOD("remove_valid_right_disconnect_type", "type"), &GraphEdit::remove_valid_right_disconnect_type);
@@ -2324,11 +1805,14 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_show_zoom_label", "enable"), &GraphEdit::set_show_zoom_label);
ClassDB::bind_method(D_METHOD("is_showing_zoom_label"), &GraphEdit::is_showing_zoom_label);
- ClassDB::bind_method(D_METHOD("set_snap", "pixels"), &GraphEdit::set_snap);
- ClassDB::bind_method(D_METHOD("get_snap"), &GraphEdit::get_snap);
+ ClassDB::bind_method(D_METHOD("set_show_grid", "enable"), &GraphEdit::set_show_grid);
+ ClassDB::bind_method(D_METHOD("is_showing_grid"), &GraphEdit::is_showing_grid);
+
+ ClassDB::bind_method(D_METHOD("set_snapping_enabled", "enable"), &GraphEdit::set_snapping_enabled);
+ ClassDB::bind_method(D_METHOD("is_snapping_enabled"), &GraphEdit::is_snapping_enabled);
- ClassDB::bind_method(D_METHOD("set_use_snap", "enable"), &GraphEdit::set_use_snap);
- ClassDB::bind_method(D_METHOD("is_using_snap"), &GraphEdit::is_using_snap);
+ ClassDB::bind_method(D_METHOD("set_snapping_distance", "pixels"), &GraphEdit::set_snapping_distance);
+ ClassDB::bind_method(D_METHOD("get_snapping_distance"), &GraphEdit::get_snapping_distance);
ClassDB::bind_method(D_METHOD("set_connection_lines_curvature", "curvature"), &GraphEdit::set_connection_lines_curvature);
ClassDB::bind_method(D_METHOD("get_connection_lines_curvature"), &GraphEdit::get_connection_lines_curvature);
@@ -2353,11 +1837,10 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_right_disconnects", "enable"), &GraphEdit::set_right_disconnects);
ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled);
- ClassDB::bind_method(D_METHOD("_update_scroll_offset"), &GraphEdit::_update_scroll_offset);
GDVIRTUAL_BIND(_is_in_input_hotzone, "in_node", "in_port", "mouse_position");
GDVIRTUAL_BIND(_is_in_output_hotzone, "in_node", "in_port", "mouse_position");
- ClassDB::bind_method(D_METHOD("get_zoom_hbox"), &GraphEdit::get_zoom_hbox);
+ ClassDB::bind_method(D_METHOD("get_menu_hbox"), &GraphEdit::get_menu_hbox);
ClassDB::bind_method(D_METHOD("arrange_nodes"), &GraphEdit::arrange_nodes);
@@ -2367,9 +1850,10 @@ void GraphEdit::_bind_methods() {
GDVIRTUAL_BIND(_is_node_hover_valid, "from_node", "from_port", "to_node", "to_port");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_scroll_ofs", "get_scroll_ofs");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance", PROPERTY_HINT_NONE, "suffix:px"), "set_snap", "get_snap");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_scroll_offset", "get_scroll_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_grid"), "set_show_grid", "is_showing_grid");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snapping_enabled"), "set_snapping_enabled", "is_snapping_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "snapping_distance", PROPERTY_HINT_NONE, "suffix:px"), "set_snapping_distance", "get_snapping_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans"), "set_panning_scheme", "get_panning_scheme");
ADD_GROUP("Connection Lines", "connection_lines");
@@ -2435,93 +1919,103 @@ GraphEdit::GraphEdit() {
top_layer->connect("focus_exited", callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
connections_layer = memnew(Control);
- add_child(connections_layer, false, INTERNAL_MODE_FRONT);
+ add_child(connections_layer, false);
connections_layer->connect("draw", callable_mp(this, &GraphEdit::_connections_layer_draw));
- connections_layer->set_name("CLAYER");
- connections_layer->set_disable_visibility_clip(true); // so it can draw freely and be offset
+ connections_layer->set_name("_connection_layer");
+ connections_layer->set_disable_visibility_clip(true); // Necessary, so it can draw freely and be offset.
connections_layer->set_mouse_filter(MOUSE_FILTER_IGNORE);
- h_scroll = memnew(HScrollBar);
- h_scroll->set_name("_h_scroll");
- top_layer->add_child(h_scroll);
+ h_scrollbar = memnew(HScrollBar);
+ h_scrollbar->set_name("_h_scroll");
+ top_layer->add_child(h_scrollbar);
- v_scroll = memnew(VScrollBar);
- v_scroll->set_name("_v_scroll");
- top_layer->add_child(v_scroll);
+ v_scrollbar = memnew(VScrollBar);
+ v_scrollbar->set_name("_v_scroll");
+ top_layer->add_child(v_scrollbar);
- //set large minmax so it can scroll even if not resized yet
- h_scroll->set_min(-10000);
- h_scroll->set_max(10000);
+ // Set large minmax so it can scroll even if not resized yet.
+ h_scrollbar->set_min(-10000);
+ h_scrollbar->set_max(10000);
- v_scroll->set_min(-10000);
- v_scroll->set_max(10000);
+ v_scrollbar->set_min(-10000);
+ v_scrollbar->set_max(10000);
- h_scroll->connect("value_changed", callable_mp(this, &GraphEdit::_scroll_moved));
- v_scroll->connect("value_changed", callable_mp(this, &GraphEdit::_scroll_moved));
+ h_scrollbar->connect("value_changed", callable_mp(this, &GraphEdit::_scroll_moved));
+ v_scrollbar->connect("value_changed", callable_mp(this, &GraphEdit::_scroll_moved));
- zoom_hb = memnew(HBoxContainer);
- top_layer->add_child(zoom_hb);
- zoom_hb->set_position(Vector2(10, 10));
+ menu_hbox = memnew(HBoxContainer);
+ top_layer->add_child(menu_hbox);
+ menu_hbox->set_position(Vector2(10, 10));
zoom_label = memnew(Label);
- zoom_hb->add_child(zoom_label);
+ menu_hbox->add_child(zoom_label);
zoom_label->set_visible(false);
zoom_label->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
zoom_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
zoom_label->set_custom_minimum_size(Size2(48, 0));
_update_zoom_label();
- zoom_minus = memnew(Button);
- zoom_minus->set_flat(true);
- zoom_hb->add_child(zoom_minus);
- zoom_minus->set_tooltip_text(RTR("Zoom Out"));
- zoom_minus->connect("pressed", callable_mp(this, &GraphEdit::_zoom_minus));
- zoom_minus->set_focus_mode(FOCUS_NONE);
-
- zoom_reset = memnew(Button);
- zoom_reset->set_flat(true);
- zoom_hb->add_child(zoom_reset);
- zoom_reset->set_tooltip_text(RTR("Zoom Reset"));
- zoom_reset->connect("pressed", callable_mp(this, &GraphEdit::_zoom_reset));
- zoom_reset->set_focus_mode(FOCUS_NONE);
-
- zoom_plus = memnew(Button);
- zoom_plus->set_flat(true);
- zoom_hb->add_child(zoom_plus);
- zoom_plus->set_tooltip_text(RTR("Zoom In"));
- zoom_plus->connect("pressed", callable_mp(this, &GraphEdit::_zoom_plus));
- zoom_plus->set_focus_mode(FOCUS_NONE);
-
- snap_button = memnew(Button);
- snap_button->set_flat(true);
- snap_button->set_toggle_mode(true);
- snap_button->set_tooltip_text(RTR("Enable snap and show grid."));
- snap_button->connect("pressed", callable_mp(this, &GraphEdit::_snap_toggled));
- snap_button->set_pressed(true);
- snap_button->set_focus_mode(FOCUS_NONE);
- zoom_hb->add_child(snap_button);
-
- snap_amount = memnew(SpinBox);
- snap_amount->set_min(5);
- snap_amount->set_max(100);
- snap_amount->set_step(1);
- snap_amount->set_value(20);
- snap_amount->connect("value_changed", callable_mp(this, &GraphEdit::_snap_value_changed));
- zoom_hb->add_child(snap_amount);
+ zoom_minus_button = memnew(Button);
+ zoom_minus_button->set_flat(true);
+ menu_hbox->add_child(zoom_minus_button);
+ zoom_minus_button->set_tooltip_text(RTR("Zoom Out"));
+ zoom_minus_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_minus));
+ zoom_minus_button->set_focus_mode(FOCUS_NONE);
+
+ zoom_reset_button = memnew(Button);
+ zoom_reset_button->set_flat(true);
+ menu_hbox->add_child(zoom_reset_button);
+ zoom_reset_button->set_tooltip_text(RTR("Zoom Reset"));
+ zoom_reset_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_reset));
+ zoom_reset_button->set_focus_mode(FOCUS_NONE);
+
+ zoom_plus_button = memnew(Button);
+ zoom_plus_button->set_flat(true);
+ menu_hbox->add_child(zoom_plus_button);
+ zoom_plus_button->set_tooltip_text(RTR("Zoom In"));
+ zoom_plus_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_plus));
+ zoom_plus_button->set_focus_mode(FOCUS_NONE);
+
+ show_grid_button = memnew(Button);
+ show_grid_button->set_flat(true);
+ show_grid_button->set_toggle_mode(true);
+ show_grid_button->set_tooltip_text(RTR("Toggle the visual grid."));
+ show_grid_button->connect("pressed", callable_mp(this, &GraphEdit::_show_grid_toggled));
+ show_grid_button->set_pressed(true);
+ show_grid_button->set_focus_mode(FOCUS_NONE);
+ menu_hbox->add_child(show_grid_button);
+
+ toggle_snapping_button = memnew(Button);
+ toggle_snapping_button->set_flat(true);
+ toggle_snapping_button->set_toggle_mode(true);
+ toggle_snapping_button->set_tooltip_text(RTR("Toggle snapping to the grid."));
+ toggle_snapping_button->connect("pressed", callable_mp(this, &GraphEdit::_snapping_toggled));
+ toggle_snapping_button->set_pressed(snapping_enabled);
+ toggle_snapping_button->set_focus_mode(FOCUS_NONE);
+ menu_hbox->add_child(toggle_snapping_button);
+
+ snapping_distance_spinbox = memnew(SpinBox);
+ snapping_distance_spinbox->set_min(GRID_MIN_SNAPPING_DISTANCE);
+ snapping_distance_spinbox->set_max(GRID_MAX_SNAPPING_DISTANCE);
+ snapping_distance_spinbox->set_step(1);
+ snapping_distance_spinbox->set_value(snapping_distance);
+ snapping_distance_spinbox->set_tooltip_text(RTR("Change the snapping distance."));
+ snapping_distance_spinbox->connect("value_changed", callable_mp(this, &GraphEdit::_snapping_distance_changed));
+ menu_hbox->add_child(snapping_distance_spinbox);
minimap_button = memnew(Button);
minimap_button->set_flat(true);
minimap_button->set_toggle_mode(true);
- minimap_button->set_tooltip_text(RTR("Enable grid minimap."));
+ minimap_button->set_tooltip_text(RTR("Toggle the graph minimap."));
minimap_button->connect("pressed", callable_mp(this, &GraphEdit::_minimap_toggled));
- minimap_button->set_pressed(true);
+ minimap_button->set_pressed(show_grid);
minimap_button->set_focus_mode(FOCUS_NONE);
- zoom_hb->add_child(minimap_button);
+ menu_hbox->add_child(minimap_button);
layout_button = memnew(Button);
layout_button->set_flat(true);
- zoom_hb->add_child(layout_button);
- layout_button->set_tooltip_text(RTR("Arrange nodes."));
+ menu_hbox->add_child(layout_button);
+ layout_button->set_tooltip_text(RTR("Automatically arrange selected nodes."));
layout_button->connect("pressed", callable_mp(this, &GraphEdit::arrange_nodes));
layout_button->set_focus_mode(FOCUS_NONE);
@@ -2536,11 +2030,13 @@ GraphEdit::GraphEdit() {
minimap->set_custom_minimum_size(Vector2(50, 50));
minimap->set_size(minimap_size);
minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
- minimap->set_offset(Side::SIDE_LEFT, -minimap_size.x - MINIMAP_OFFSET);
- minimap->set_offset(Side::SIDE_TOP, -minimap_size.y - MINIMAP_OFFSET);
+ minimap->set_offset(Side::SIDE_LEFT, -minimap_size.width - MINIMAP_OFFSET);
+ minimap->set_offset(Side::SIDE_TOP, -minimap_size.height - MINIMAP_OFFSET);
minimap->set_offset(Side::SIDE_RIGHT, -MINIMAP_OFFSET);
minimap->set_offset(Side::SIDE_BOTTOM, -MINIMAP_OFFSET);
minimap->connect("draw", callable_mp(this, &GraphEdit::_minimap_draw));
set_clip_contents(true);
+
+ arranger = Ref<GraphEditArranger>(memnew(GraphEditArranger(this)));
}
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 8b02fbfddc..614e9b9695 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -39,6 +39,7 @@
#include "scene/gui/spin_box.h"
class GraphEdit;
+class GraphEditArranger;
class ViewPanner;
class GraphEditFilter : public Control {
@@ -47,6 +48,7 @@ class GraphEditFilter : public Control {
friend class GraphEdit;
friend class GraphEditMinimap;
GraphEdit *ge = nullptr;
+
virtual bool has_point(const Point2 &p_point) const override;
public:
@@ -58,9 +60,9 @@ class GraphEditMinimap : public Control {
friend class GraphEdit;
friend class GraphEditFilter;
+
GraphEdit *ge = nullptr;
-protected:
public:
GraphEditMinimap(GraphEdit *p_edit);
@@ -97,8 +99,8 @@ class GraphEdit : public Control {
public:
struct Connection {
- StringName from;
- StringName to;
+ StringName from_node;
+ StringName to_node;
int from_port = 0;
int to_port = 0;
float activity = 0.0;
@@ -111,33 +113,57 @@ public:
};
private:
- Label *zoom_label = nullptr;
- Button *zoom_minus = nullptr;
- Button *zoom_reset = nullptr;
- Button *zoom_plus = nullptr;
+ struct ConnectionType {
+ union {
+ struct {
+ uint32_t type_a;
+ uint32_t type_b;
+ };
+ uint64_t key = 0;
+ };
- Button *snap_button = nullptr;
- SpinBox *snap_amount = nullptr;
+ static uint32_t hash(const ConnectionType &p_conn) {
+ return hash_one_uint64(p_conn.key);
+ }
+ bool operator==(const ConnectionType &p_type) const {
+ return key == p_type.key;
+ }
+
+ ConnectionType(uint32_t a = 0, uint32_t b = 0) {
+ type_a = a;
+ type_b = b;
+ }
+ };
+
+ Label *zoom_label = nullptr;
+ Button *zoom_minus_button = nullptr;
+ Button *zoom_reset_button = nullptr;
+ Button *zoom_plus_button = nullptr;
+ Button *toggle_snapping_button = nullptr;
+ SpinBox *snapping_distance_spinbox = nullptr;
+ Button *show_grid_button = nullptr;
Button *minimap_button = nullptr;
Button *layout_button = nullptr;
- HScrollBar *h_scroll = nullptr;
- VScrollBar *v_scroll = nullptr;
+ HScrollBar *h_scrollbar = nullptr;
+ VScrollBar *v_scrollbar = nullptr;
float port_hotzone_inner_extent = 0.0;
float port_hotzone_outer_extent = 0.0;
Ref<ViewPanner> panner;
bool warped_panning = true;
- void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
- void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
bool arrange_nodes_button_hidden = false;
+ bool snapping_enabled = true;
+ int snapping_distance = 20;
+ bool show_grid = true;
+
bool connecting = false;
- StringName connecting_from;
+ String connecting_from;
bool connecting_out = false;
int connecting_index = 0;
int connecting_type = 0;
@@ -146,8 +172,10 @@ private:
Vector2 connecting_to;
StringName connecting_target_to;
int connecting_target_index = 0;
+
bool just_disconnected = false;
bool connecting_valid = false;
+
Vector2 click_pos;
PanningScheme panning_scheme = SCROLL_ZOOMS;
@@ -162,19 +190,14 @@ private:
float zoom_min = 0.0;
float zoom_max = 0.0;
- void _zoom_minus();
- void _zoom_reset();
- void _zoom_plus();
- void _update_zoom_label();
-
bool box_selecting = false;
bool box_selection_mode_additive = false;
Point2 box_selecting_from;
Point2 box_selecting_to;
Rect2 box_selecting_rect;
- List<GraphNode *> previous_selected;
+ List<GraphNode *> prev_selected;
- bool setting_scroll_ofs = false;
+ bool setting_scroll_offset = false;
bool right_disconnects = false;
bool updating = false;
bool awaiting_scroll_offset_update = false;
@@ -184,102 +207,69 @@ private:
float lines_curvature = 0.5f;
bool lines_antialiased = true;
+ HBoxContainer *menu_hbox = nullptr;
+ Control *connections_layer = nullptr;
+ GraphEditFilter *top_layer = nullptr;
+ GraphEditMinimap *minimap = nullptr;
+
+ Ref<GraphEditArranger> arranger;
+
+ HashSet<ConnectionType, ConnectionType> valid_connection_types;
+ HashSet<int> valid_left_disconnect_types;
+ HashSet<int> valid_right_disconnect_types;
+
+ void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
+ void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
+ void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
+
+ void _zoom_minus();
+ void _zoom_reset();
+ void _zoom_plus();
+ void _update_zoom_label();
+
PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to);
void _draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_zoom);
void _graph_node_selected(Node *p_gn);
void _graph_node_deselected(Node *p_gn);
- void _graph_node_raised(Node *p_gn);
+ void _graph_node_moved_to_front(Node *p_gn);
+ void _graph_node_resized(Vector2 p_new_minsize, Node *p_gn);
void _graph_node_moved(Node *p_gn);
void _graph_node_slot_updated(int p_index, Node *p_gn);
void _update_scroll();
+ void _update_scroll_offset();
void _scroll_moved(double);
virtual void gui_input(const Ref<InputEvent> &p_ev) override;
-
- Control *connections_layer = nullptr;
- GraphEditFilter *top_layer = nullptr;
- GraphEditMinimap *minimap = nullptr;
void _top_layer_input(const Ref<InputEvent> &p_ev);
- bool is_in_input_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
- bool is_in_output_hotzone(GraphNode *p_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
- bool is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
+ bool is_in_input_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
+ bool is_in_output_hotzone(GraphNode *p_graph_node, int p_port, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
+ bool is_in_port_hotzone(const Vector2 &p_pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
void _top_layer_draw();
void _connections_layer_draw();
void _minimap_draw();
- void _update_scroll_offset();
TypedArray<Dictionary> _get_connection_list() const;
- bool lines_on_bg = false;
-
- struct ConnType {
- union {
- struct {
- uint32_t type_a;
- uint32_t type_b;
- };
- uint64_t key = 0;
- };
-
- static uint32_t hash(const ConnType &p_conn) {
- return hash_one_uint64(p_conn.key);
- }
- bool operator==(const ConnType &p_type) const {
- return key == p_type.key;
- }
-
- ConnType(uint32_t a = 0, uint32_t b = 0) {
- type_a = a;
- type_b = b;
- }
- };
-
- HashSet<ConnType, ConnType> valid_connection_types;
- HashSet<int> valid_left_disconnect_types;
- HashSet<int> valid_right_disconnect_types;
-
- HashMap<StringName, Vector<GraphNode *>> comment_enclosed_nodes;
- void _update_comment_enclosed_nodes_list(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes);
- void _set_drag_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, bool p_drag);
- void _set_position_of_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, Vector2 p_pos);
-
- HBoxContainer *zoom_hb = nullptr;
-
friend class GraphEditFilter;
bool _filter_input(const Point2 &p_point);
- void _snap_toggled();
- void _snap_value_changed(double);
+ void _snapping_toggled();
+ void _snapping_distance_changed(double);
+ void _show_grid_toggled();
friend class GraphEditMinimap;
void _minimap_toggled();
bool _check_clickable_control(Control *p_control, const Vector2 &r_mouse_pos, const Vector2 &p_offset);
- bool arranging_graph = false;
-
- enum SET_OPERATIONS {
- IS_EQUAL,
- IS_SUBSET,
- DIFFERENCE,
- UNION,
- };
-
- int _set_operations(SET_OPERATIONS p_operation, HashSet<StringName> &r_u, const HashSet<StringName> &r_v);
- HashMap<int, Vector<StringName>> _layering(const HashSet<StringName> &r_selected_nodes, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours);
- Vector<StringName> _split(const Vector<StringName> &r_layer, const HashMap<StringName, Dictionary> &r_crossings);
- void _horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours, const HashSet<StringName> &r_selected_nodes);
- void _crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours);
- void _calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const HashSet<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info);
- float _calculate_threshold(StringName p_v, StringName p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions);
- void _place_block(StringName p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions);
-
protected:
static void _bind_methods();
+
virtual void add_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
+
void _notification(int p_what);
GDVIRTUAL2RC(Vector<Vector2>, _get_connection_line, Vector2, Vector2)
@@ -337,6 +327,7 @@ public:
GraphEditFilter *get_top_layer() const { return top_layer; }
GraphEditMinimap *get_minimap() const { return minimap; }
+
void get_connection_list(List<Connection> *r_connections) const;
void set_right_disconnects(bool p_enable);
@@ -348,16 +339,19 @@ public:
void add_valid_left_disconnect_type(int p_type);
void remove_valid_left_disconnect_type(int p_type);
- void set_scroll_ofs(const Vector2 &p_ofs);
- Vector2 get_scroll_ofs() const;
+ void set_scroll_offset(const Vector2 &p_ofs);
+ Vector2 get_scroll_offset() const;
void set_selected(Node *p_child);
- void set_use_snap(bool p_enable);
- bool is_using_snap() const;
+ void set_snapping_enabled(bool p_enable);
+ bool is_snapping_enabled() const;
+
+ void set_snapping_distance(int p_snapping_distance);
+ int get_snapping_distance() const;
- int get_snap() const;
- void set_snap(int p_snap);
+ void set_show_grid(bool p_enable);
+ bool is_showing_grid() const;
void set_connection_lines_curvature(float p_curvature);
float get_connection_lines_curvature() const;
@@ -368,7 +362,7 @@ public:
void set_connection_lines_antialiased(bool p_antialiased);
bool is_connection_lines_antialiased() const;
- HBoxContainer *get_zoom_hbox();
+ HBoxContainer *get_menu_hbox();
Ref<ViewPanner> get_panner();
void set_warped_panning(bool p_warped);
diff --git a/scene/gui/graph_edit_arranger.cpp b/scene/gui/graph_edit_arranger.cpp
new file mode 100644
index 0000000000..f4d9dcbf95
--- /dev/null
+++ b/scene/gui/graph_edit_arranger.cpp
@@ -0,0 +1,565 @@
+/**************************************************************************/
+/* graph_edit_arranger.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 "graph_edit_arranger.h"
+
+#include "scene/gui/graph_edit.h"
+
+void GraphEditArranger::arrange_nodes() {
+ ERR_FAIL_COND(!graph_edit);
+
+ if (!arranging_graph) {
+ arranging_graph = true;
+ } else {
+ return;
+ }
+
+ Dictionary node_names;
+ HashSet<StringName> selected_nodes;
+
+ bool arrange_entire_graph = true;
+ for (int i = graph_edit->get_child_count() - 1; i >= 0; i--) {
+ GraphNode *graph_element = Object::cast_to<GraphNode>(graph_edit->get_child(i));
+ if (!graph_element) {
+ continue;
+ }
+
+ node_names[graph_element->get_name()] = graph_element;
+
+ if (graph_element->is_selected()) {
+ arrange_entire_graph = false;
+ }
+ }
+
+ HashMap<StringName, HashSet<StringName>> upper_neighbours;
+ HashMap<StringName, Pair<int, int>> port_info;
+ Vector2 origin(FLT_MAX, FLT_MAX);
+
+ float gap_v = 100.0f;
+ float gap_h = 100.0f;
+
+ for (int i = graph_edit->get_child_count() - 1; i >= 0; i--) {
+ GraphNode *graph_element = Object::cast_to<GraphNode>(graph_edit->get_child(i));
+ if (!graph_element) {
+ continue;
+ }
+
+ if (graph_element->is_selected() || arrange_entire_graph) {
+ selected_nodes.insert(graph_element->get_name());
+ HashSet<StringName> s;
+ List<GraphEdit::Connection> connection_list;
+ graph_edit->get_connection_list(&connection_list);
+ for (List<GraphEdit::Connection>::Element *E = connection_list.front(); E; E = E->next()) {
+ GraphNode *p_from = Object::cast_to<GraphNode>(node_names[E->get().from_node]);
+ if (E->get().to_node == graph_element->get_name() && (p_from->is_selected() || arrange_entire_graph) && E->get().to_node != E->get().from_node) {
+ if (!s.has(p_from->get_name())) {
+ s.insert(p_from->get_name());
+ }
+ String s_connection = String(p_from->get_name()) + " " + String(E->get().to_node);
+ StringName _connection(s_connection);
+ Pair<int, int> ports(E->get().from_port, E->get().to_port);
+ if (port_info.has(_connection)) {
+ Pair<int, int> p_ports = port_info[_connection];
+ if (p_ports.first < ports.first) {
+ ports = p_ports;
+ }
+ }
+ port_info.insert(_connection, ports);
+ }
+ }
+ upper_neighbours.insert(graph_element->get_name(), s);
+ }
+ }
+
+ if (!selected_nodes.size()) {
+ arranging_graph = false;
+ return;
+ }
+
+ HashMap<int, Vector<StringName>> layers = _layering(selected_nodes, upper_neighbours);
+ _crossing_minimisation(layers, upper_neighbours);
+
+ Dictionary root, align, sink, shift;
+ _horizontal_alignment(root, align, layers, upper_neighbours, selected_nodes);
+
+ HashMap<StringName, Vector2> new_positions;
+ Vector2 default_position(FLT_MAX, FLT_MAX);
+ Dictionary inner_shift;
+ HashSet<StringName> block_heads;
+
+ for (const StringName &E : selected_nodes) {
+ inner_shift[E] = 0.0f;
+ sink[E] = E;
+ shift[E] = FLT_MAX;
+ new_positions.insert(E, default_position);
+ if ((StringName)root[E] == E) {
+ block_heads.insert(E);
+ }
+ }
+
+ _calculate_inner_shifts(inner_shift, root, node_names, align, block_heads, port_info);
+
+ for (const StringName &E : block_heads) {
+ _place_block(E, gap_v, layers, root, align, node_names, inner_shift, sink, shift, new_positions);
+ }
+ origin.y = Object::cast_to<GraphNode>(node_names[layers[0][0]])->get_position_offset().y - (new_positions[layers[0][0]].y + (float)inner_shift[layers[0][0]]);
+ origin.x = Object::cast_to<GraphNode>(node_names[layers[0][0]])->get_position_offset().x;
+
+ for (const StringName &E : block_heads) {
+ StringName u = E;
+ float start_from = origin.y + new_positions[E].y;
+ do {
+ Vector2 cal_pos;
+ cal_pos.y = start_from + (real_t)inner_shift[u];
+ new_positions.insert(u, cal_pos);
+ u = align[u];
+ } while (u != E);
+ }
+
+ // Compute horizontal coordinates individually for layers to get uniform gap.
+ float start_from = origin.x;
+ float largest_node_size = 0.0f;
+
+ for (unsigned int i = 0; i < layers.size(); i++) {
+ Vector<StringName> layer = layers[i];
+ for (int j = 0; j < layer.size(); j++) {
+ float current_node_size = Object::cast_to<GraphNode>(node_names[layer[j]])->get_size().x;
+ largest_node_size = MAX(largest_node_size, current_node_size);
+ }
+
+ for (int j = 0; j < layer.size(); j++) {
+ float current_node_size = Object::cast_to<GraphNode>(node_names[layer[j]])->get_size().x;
+ Vector2 cal_pos = new_positions[layer[j]];
+
+ if (current_node_size == largest_node_size) {
+ cal_pos.x = start_from;
+ } else {
+ float current_node_start_pos = start_from;
+ if (current_node_size < largest_node_size / 2) {
+ if (!(i || j)) {
+ start_from -= (largest_node_size - current_node_size);
+ }
+ current_node_start_pos = start_from + largest_node_size - current_node_size;
+ }
+ cal_pos.x = current_node_start_pos;
+ }
+ new_positions.insert(layer[j], cal_pos);
+ }
+
+ start_from += largest_node_size + gap_h;
+ largest_node_size = 0.0f;
+ }
+
+ graph_edit->emit_signal(SNAME("begin_node_move"));
+ for (const StringName &E : selected_nodes) {
+ GraphNode *graph_node = Object::cast_to<GraphNode>(node_names[E]);
+ graph_node->set_drag(true);
+ Vector2 pos = (new_positions[E]);
+
+ if (graph_edit->is_snapping_enabled()) {
+ float snapping_distance = graph_edit->get_snapping_distance();
+ pos = pos.snapped(Vector2(snapping_distance, snapping_distance));
+ }
+ graph_node->set_position_offset(pos);
+ graph_node->set_drag(false);
+ }
+ graph_edit->emit_signal(SNAME("end_node_move"));
+ arranging_graph = false;
+}
+
+int GraphEditArranger::_set_operations(SET_OPERATIONS p_operation, HashSet<StringName> &r_u, const HashSet<StringName> &r_v) {
+ switch (p_operation) {
+ case GraphEditArranger::IS_EQUAL: {
+ for (const StringName &E : r_u) {
+ if (!r_v.has(E)) {
+ return 0;
+ }
+ }
+ return r_u.size() == r_v.size();
+ } break;
+ case GraphEditArranger::IS_SUBSET: {
+ if (r_u.size() == r_v.size() && !r_u.size()) {
+ return 1;
+ }
+ for (const StringName &E : r_u) {
+ if (!r_v.has(E)) {
+ return 0;
+ }
+ }
+ return 1;
+ } break;
+ case GraphEditArranger::DIFFERENCE: {
+ for (HashSet<StringName>::Iterator E = r_u.begin(); E;) {
+ HashSet<StringName>::Iterator N = E;
+ ++N;
+ if (r_v.has(*E)) {
+ r_u.remove(E);
+ }
+ E = N;
+ }
+ return r_u.size();
+ } break;
+ case GraphEditArranger::UNION: {
+ for (const StringName &E : r_v) {
+ if (!r_u.has(E)) {
+ r_u.insert(E);
+ }
+ }
+ return r_u.size();
+ } break;
+ default:
+ break;
+ }
+ return -1;
+}
+
+HashMap<int, Vector<StringName>> GraphEditArranger::_layering(const HashSet<StringName> &r_selected_nodes, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours) {
+ HashMap<int, Vector<StringName>> l;
+
+ HashSet<StringName> p = r_selected_nodes, q = r_selected_nodes, u, z;
+ int current_layer = 0;
+ bool selected = false;
+
+ while (!_set_operations(GraphEditArranger::IS_EQUAL, q, u)) {
+ _set_operations(GraphEditArranger::DIFFERENCE, p, u);
+ for (const StringName &E : p) {
+ HashSet<StringName> n = r_upper_neighbours[E];
+ if (_set_operations(GraphEditArranger::IS_SUBSET, n, z)) {
+ Vector<StringName> t;
+ t.push_back(E);
+ if (!l.has(current_layer)) {
+ l.insert(current_layer, Vector<StringName>{});
+ }
+ selected = true;
+ t.append_array(l[current_layer]);
+ l.insert(current_layer, t);
+ HashSet<StringName> V;
+ V.insert(E);
+ _set_operations(GraphEditArranger::UNION, u, V);
+ }
+ }
+ if (!selected) {
+ current_layer++;
+ uint32_t previous_size_z = z.size();
+ _set_operations(GraphEditArranger::UNION, z, u);
+ if (z.size() == previous_size_z) {
+ WARN_PRINT("Graph contains cycle(s). The cycle(s) will not be rearranged accurately.");
+ Vector<StringName> t;
+ if (l.has(0)) {
+ t.append_array(l[0]);
+ }
+ for (const StringName &E : p) {
+ t.push_back(E);
+ }
+ l.insert(0, t);
+ break;
+ }
+ }
+ selected = false;
+ }
+
+ return l;
+}
+
+Vector<StringName> GraphEditArranger::_split(const Vector<StringName> &r_layer, const HashMap<StringName, Dictionary> &r_crossings) {
+ if (!r_layer.size()) {
+ return Vector<StringName>();
+ }
+
+ StringName p = r_layer[Math::random(0, r_layer.size() - 1)];
+ Vector<StringName> left;
+ Vector<StringName> right;
+
+ for (int i = 0; i < r_layer.size(); i++) {
+ if (p != r_layer[i]) {
+ StringName q = r_layer[i];
+ int cross_pq = r_crossings[p][q];
+ int cross_qp = r_crossings[q][p];
+ if (cross_pq > cross_qp) {
+ left.push_back(q);
+ } else {
+ right.push_back(q);
+ }
+ }
+ }
+
+ left.push_back(p);
+ left.append_array(right);
+ return left;
+}
+
+void GraphEditArranger::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours, const HashSet<StringName> &r_selected_nodes) {
+ for (const StringName &E : r_selected_nodes) {
+ r_root[E] = E;
+ r_align[E] = E;
+ }
+
+ if (r_layers.size() == 1) {
+ return;
+ }
+
+ for (unsigned int i = 1; i < r_layers.size(); i++) {
+ Vector<StringName> lower_layer = r_layers[i];
+ Vector<StringName> upper_layer = r_layers[i - 1];
+ int r = -1;
+
+ for (int j = 0; j < lower_layer.size(); j++) {
+ Vector<Pair<int, StringName>> up;
+ StringName current_node = lower_layer[j];
+ for (int k = 0; k < upper_layer.size(); k++) {
+ StringName adjacent_neighbour = upper_layer[k];
+ if (r_upper_neighbours[current_node].has(adjacent_neighbour)) {
+ up.push_back(Pair<int, StringName>(k, adjacent_neighbour));
+ }
+ }
+
+ int start = (up.size() - 1) / 2;
+ int end = (up.size() - 1) % 2 ? start + 1 : start;
+ for (int p = start; p <= end; p++) {
+ StringName Align = r_align[current_node];
+ if (Align == current_node && r < up[p].first) {
+ r_align[up[p].second] = lower_layer[j];
+ r_root[current_node] = r_root[up[p].second];
+ r_align[current_node] = r_root[up[p].second];
+ r = up[p].first;
+ }
+ }
+ }
+ }
+}
+
+void GraphEditArranger::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours) {
+ if (r_layers.size() == 1) {
+ return;
+ }
+
+ for (unsigned int i = 1; i < r_layers.size(); i++) {
+ Vector<StringName> upper_layer = r_layers[i - 1];
+ Vector<StringName> lower_layer = r_layers[i];
+ HashMap<StringName, Dictionary> c;
+
+ for (int j = 0; j < lower_layer.size(); j++) {
+ StringName p = lower_layer[j];
+ Dictionary d;
+
+ for (int k = 0; k < lower_layer.size(); k++) {
+ unsigned int crossings = 0;
+ StringName q = lower_layer[k];
+
+ if (j != k) {
+ for (int h = 1; h < upper_layer.size(); h++) {
+ if (r_upper_neighbours[p].has(upper_layer[h])) {
+ for (int g = 0; g < h; g++) {
+ if (r_upper_neighbours[q].has(upper_layer[g])) {
+ crossings++;
+ }
+ }
+ }
+ }
+ }
+ d[q] = crossings;
+ }
+ c.insert(p, d);
+ }
+
+ r_layers.insert(i, _split(lower_layer, c));
+ }
+}
+
+void GraphEditArranger::_calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const HashSet<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info) {
+ for (const StringName &E : r_block_heads) {
+ real_t left = 0;
+ StringName u = E;
+ StringName v = r_align[u];
+ while (u != v && (StringName)r_root[u] != v) {
+ String _connection = String(u) + " " + String(v);
+
+ GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[u]);
+ GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[v]);
+
+ Pair<int, int> ports = r_port_info[_connection];
+ int port_from = ports.first;
+ int port_to = ports.second;
+
+ Vector2 pos_from = gnode_from->get_connection_output_position(port_from) * graph_edit->get_zoom();
+ Vector2 pos_to = gnode_to->get_connection_input_position(port_to) * graph_edit->get_zoom();
+
+ real_t s = (real_t)r_inner_shifts[u] + (pos_from.y - pos_to.y) / graph_edit->get_zoom();
+ r_inner_shifts[v] = s;
+ left = MIN(left, s);
+
+ u = v;
+ v = (StringName)r_align[v];
+ }
+
+ u = E;
+ do {
+ r_inner_shifts[u] = (real_t)r_inner_shifts[u] - left;
+ u = (StringName)r_align[u];
+ } while (u != E);
+ }
+}
+
+float GraphEditArranger::_calculate_threshold(StringName p_v, StringName p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions) {
+#define MAX_ORDER 2147483647
+#define ORDER(node, layers) \
+ for (unsigned int i = 0; i < layers.size(); i++) { \
+ int index = layers[i].find(node); \
+ if (index > 0) { \
+ order = index; \
+ break; \
+ } \
+ order = MAX_ORDER; \
+ }
+
+ int order = MAX_ORDER;
+ float threshold = p_current_threshold;
+ if (p_v == p_w) {
+ int min_order = MAX_ORDER;
+ GraphEdit::Connection incoming;
+ List<GraphEdit::Connection> connection_list;
+ graph_edit->get_connection_list(&connection_list);
+ for (List<GraphEdit::Connection>::Element *E = connection_list.front(); E; E = E->next()) {
+ if (E->get().to_node == p_w) {
+ ORDER(E->get().from_node, r_layers);
+ if (min_order > order) {
+ min_order = order;
+ incoming = E->get();
+ }
+ }
+ }
+
+ if (incoming.from_node != StringName()) {
+ GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[incoming.from_node]);
+ GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[p_w]);
+ Vector2 pos_from = gnode_from->get_connection_output_position(incoming.from_port) * graph_edit->get_zoom();
+ Vector2 pos_to = gnode_to->get_connection_input_position(incoming.to_port) * graph_edit->get_zoom();
+
+ // If connected block node is selected, calculate thershold or add current block to list.
+ if (gnode_from->is_selected()) {
+ Vector2 connected_block_pos = r_node_positions[r_root[incoming.from_node]];
+ if (connected_block_pos.y != FLT_MAX) {
+ //Connected block is placed, calculate threshold.
+ threshold = connected_block_pos.y + (real_t)r_inner_shift[incoming.from_node] - (real_t)r_inner_shift[p_w] + pos_from.y - pos_to.y;
+ }
+ }
+ }
+ }
+ if (threshold == FLT_MIN && (StringName)r_align[p_w] == p_v) {
+ // This time, pick an outgoing edge and repeat as above!
+ int min_order = MAX_ORDER;
+ GraphEdit::Connection outgoing;
+ List<GraphEdit::Connection> connection_list;
+ graph_edit->get_connection_list(&connection_list);
+ for (List<GraphEdit::Connection>::Element *E = connection_list.front(); E; E = E->next()) {
+ if (E->get().from_node == p_w) {
+ ORDER(E->get().to_node, r_layers);
+ if (min_order > order) {
+ min_order = order;
+ outgoing = E->get();
+ }
+ }
+ }
+
+ if (outgoing.to_node != StringName()) {
+ GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[p_w]);
+ GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[outgoing.to_node]);
+ Vector2 pos_from = gnode_from->get_connection_output_position(outgoing.from_port) * graph_edit->get_zoom();
+ Vector2 pos_to = gnode_to->get_connection_input_position(outgoing.to_port) * graph_edit->get_zoom();
+
+ // If connected block node is selected, calculate thershold or add current block to list.
+ if (gnode_to->is_selected()) {
+ Vector2 connected_block_pos = r_node_positions[r_root[outgoing.to_node]];
+ if (connected_block_pos.y != FLT_MAX) {
+ //Connected block is placed. Calculate threshold
+ threshold = connected_block_pos.y + (real_t)r_inner_shift[outgoing.to_node] - (real_t)r_inner_shift[p_w] + pos_from.y - pos_to.y;
+ }
+ }
+ }
+ }
+#undef MAX_ORDER
+#undef ORDER
+ return threshold;
+}
+
+void GraphEditArranger::_place_block(StringName p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions) {
+#define PRED(node, layers) \
+ for (unsigned int i = 0; i < layers.size(); i++) { \
+ int index = layers[i].find(node); \
+ if (index > 0) { \
+ predecessor = layers[i][index - 1]; \
+ break; \
+ } \
+ predecessor = StringName(); \
+ }
+
+ StringName predecessor;
+ StringName successor;
+ Vector2 pos = r_node_positions[p_v];
+
+ if (pos.y == FLT_MAX) {
+ pos.y = 0;
+ bool initial = false;
+ StringName w = p_v;
+ real_t threshold = FLT_MIN;
+ do {
+ PRED(w, r_layers);
+ if (predecessor != StringName()) {
+ StringName u = r_root[predecessor];
+ _place_block(u, p_delta, r_layers, r_root, r_align, r_node_name, r_inner_shift, r_sink, r_shift, r_node_positions);
+ threshold = _calculate_threshold(p_v, w, r_node_name, r_layers, r_root, r_align, r_inner_shift, threshold, r_node_positions);
+ if ((StringName)r_sink[p_v] == p_v) {
+ r_sink[p_v] = r_sink[u];
+ }
+
+ Vector2 predecessor_root_pos = r_node_positions[u];
+ Vector2 predecessor_node_size = Object::cast_to<GraphNode>(r_node_name[predecessor])->get_size();
+ if (r_sink[p_v] != r_sink[u]) {
+ real_t sc = pos.y + (real_t)r_inner_shift[w] - predecessor_root_pos.y - (real_t)r_inner_shift[predecessor] - predecessor_node_size.y - p_delta;
+ r_shift[r_sink[u]] = MIN(sc, (real_t)r_shift[r_sink[u]]);
+ } else {
+ real_t sb = predecessor_root_pos.y + (real_t)r_inner_shift[predecessor] + predecessor_node_size.y - (real_t)r_inner_shift[w] + p_delta;
+ sb = MAX(sb, threshold);
+ if (initial) {
+ pos.y = sb;
+ } else {
+ pos.y = MAX(pos.y, sb);
+ }
+ initial = false;
+ }
+ }
+ threshold = _calculate_threshold(p_v, w, r_node_name, r_layers, r_root, r_align, r_inner_shift, threshold, r_node_positions);
+ w = r_align[w];
+ } while (w != p_v);
+ r_node_positions.insert(p_v, pos);
+ }
+
+#undef PRED
+}
diff --git a/scene/gui/graph_edit_arranger.h b/scene/gui/graph_edit_arranger.h
new file mode 100644
index 0000000000..e79944e5dd
--- /dev/null
+++ b/scene/gui/graph_edit_arranger.h
@@ -0,0 +1,67 @@
+/**************************************************************************/
+/* graph_edit_arranger.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 GRAPH_EDIT_ARRANGER_H
+#define GRAPH_EDIT_ARRANGER_H
+
+#include "core/object/ref_counted.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/hash_set.h"
+
+class GraphEdit;
+
+class GraphEditArranger : public RefCounted {
+ enum SET_OPERATIONS {
+ IS_EQUAL,
+ IS_SUBSET,
+ DIFFERENCE,
+ UNION,
+ };
+
+ GraphEdit *graph_edit = nullptr;
+ bool arranging_graph = false;
+
+ int _set_operations(SET_OPERATIONS p_operation, HashSet<StringName> &r_u, const HashSet<StringName> &r_v);
+ HashMap<int, Vector<StringName>> _layering(const HashSet<StringName> &r_selected_nodes, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours);
+ Vector<StringName> _split(const Vector<StringName> &r_layer, const HashMap<StringName, Dictionary> &r_crossings);
+ void _horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours, const HashSet<StringName> &r_selected_nodes);
+ void _crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours);
+ void _calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const HashSet<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info);
+ float _calculate_threshold(StringName p_v, StringName p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions);
+ void _place_block(StringName p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions);
+
+public:
+ void arrange_nodes();
+
+ GraphEditArranger(GraphEdit *p_graph_edit) :
+ graph_edit(p_graph_edit) {}
+};
+
+#endif // GRAPH_EDIT_ARRANGER_H
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index b0517caab0..2223beafda 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -286,37 +286,12 @@ void GraphNode::_resort() {
connpos_dirty = true;
}
-bool GraphNode::has_point(const Point2 &p_point) const {
- if (comment) {
- Ref<StyleBox> comment_sb = get_theme_stylebox(SNAME("comment"));
- Ref<Texture2D> resizer = get_theme_icon(SNAME("resizer"));
-
- if (Rect2(get_size() - resizer->get_size(), resizer->get_size()).has_point(p_point)) {
- return true;
- }
-
- if (Rect2(0, 0, get_size().width, comment_sb->get_margin(SIDE_TOP)).has_point(p_point)) {
- return true;
- }
-
- return false;
-
- } else {
- return Control::has_point(p_point);
- }
-}
-
void GraphNode::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_DRAW: {
Ref<StyleBox> sb;
- if (comment) {
- sb = get_theme_stylebox(selected ? SNAME("comment_focus") : SNAME("comment"));
-
- } else {
- sb = get_theme_stylebox(selected ? SNAME("selected_frame") : SNAME("frame"));
- }
+ sb = get_theme_stylebox(selected ? SNAME("selected_frame") : SNAME("frame"));
Ref<StyleBox> sb_slot = get_theme_stylebox(SNAME("slot"));
@@ -440,7 +415,7 @@ void GraphNode::_shape() {
void GraphNode::_edit_set_position(const Point2 &p_position) {
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
if (graph) {
- Point2 offset = (p_position + graph->get_scroll_ofs()) * graph->get_zoom();
+ Point2 offset = (p_position + graph->get_scroll_offset()) * graph->get_zoom();
set_position_offset(offset);
}
set_position(p_position);
@@ -1003,19 +978,6 @@ GraphNode::Overlay GraphNode::get_overlay() const {
return overlay;
}
-void GraphNode::set_comment(bool p_enable) {
- if (comment == p_enable) {
- return;
- }
-
- comment = p_enable;
- queue_redraw();
-}
-
-bool GraphNode::is_comment() const {
- return comment;
-}
-
void GraphNode::set_resizable(bool p_enable) {
if (resizable == p_enable) {
return;
@@ -1115,9 +1077,6 @@ void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_position_offset", "offset"), &GraphNode::set_position_offset);
ClassDB::bind_method(D_METHOD("get_position_offset"), &GraphNode::get_position_offset);
- ClassDB::bind_method(D_METHOD("set_comment", "comment"), &GraphNode::set_comment);
- ClassDB::bind_method(D_METHOD("is_comment"), &GraphNode::is_comment);
-
ClassDB::bind_method(D_METHOD("set_resizable", "resizable"), &GraphNode::set_resizable);
ClassDB::bind_method(D_METHOD("is_resizable"), &GraphNode::is_resizable);
@@ -1157,7 +1116,6 @@ void GraphNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draggable"), "set_draggable", "is_draggable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selectable"), "set_selectable", "is_selectable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selected"), "set_selected", "is_selected");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "comment"), "set_comment", "is_comment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "overlay", PROPERTY_HINT_ENUM, "Disabled,Breakpoint,Position"), "set_overlay", "get_overlay");
ADD_GROUP("BiDi", "");
diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h
index 7ba2e6db94..873683bc62 100644
--- a/scene/gui/graph_node.h
+++ b/scene/gui/graph_node.h
@@ -124,8 +124,6 @@ protected:
void _validate_property(PropertyInfo &p_property) const;
public:
- bool has_point(const Point2 &p_point) const override;
-
void set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left = Ref<Texture2D>(), const Ref<Texture2D> &p_custom_right = Ref<Texture2D>(), bool p_draw_stylebox = true);
void clear_slot(int p_idx);
void clear_all_slots();
@@ -189,9 +187,6 @@ public:
void set_overlay(Overlay p_overlay);
Overlay get_overlay() const;
- void set_comment(bool p_enable);
- bool is_comment() const;
-
void set_resizable(bool p_enable);
bool is_resizable() const;
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index aa9ecd4142..b273f709f2 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -1265,9 +1265,6 @@ void ItemList::_notification(int p_what) {
if (rtl) {
text_ofs.x = size.width - width;
- }
-
- if (rtl) {
items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
} else {
items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT);
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 1b48b9165d..aec9722fec 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"
@@ -300,9 +299,9 @@ void Label::_update_visible() {
int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
for (int64_t i = lines_skipped; i < last_line; i++) {
minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
- if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) {
- break;
- }
+ }
+ if (minsize.height > 0) {
+ minsize.height -= line_spacing;
}
}
@@ -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/menu_bar.cpp b/scene/gui/menu_bar.cpp
index d8ec581089..85068ac862 100644
--- a/scene/gui/menu_bar.cpp
+++ b/scene/gui/menu_bar.cpp
@@ -453,7 +453,7 @@ void MenuBar::_draw_menu_item(int p_index) {
}
Color color;
- Ref<StyleBox> style = theme_cache.normal;
+ Ref<StyleBox> style;
Rect2 item_rect = _get_menu_item_rect(p_index);
if (menu_cache[p_index].disabled) {
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 94690a4938..40db8deaac 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -244,7 +244,12 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) {
Rect2 safe_area = this_rect;
safe_area.position.y += items[p_over]._ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2;
safe_area.size.y = items[p_over]._height_cache + theme_cache.v_separation;
- DisplayServer::get_singleton()->window_set_popup_safe_rect(submenu_popup->get_window_id(), safe_area);
+ Viewport *vp = submenu_popup->get_embedder();
+ if (vp) {
+ vp->subwindow_set_popup_safe_rect(submenu_popup, safe_area);
+ } else {
+ DisplayServer::get_singleton()->window_set_popup_safe_rect(submenu_popup->get_window_id(), safe_area);
+ }
// Make the position of the parent popup relative to submenu popup.
this_rect.position = this_rect.position - submenu_pum->get_position();
@@ -273,7 +278,7 @@ void PopupMenu::_parent_focused() {
window_parent = Object::cast_to<Window>(window_parent->get_parent()->get_viewport());
}
- Rect2 safe_area = DisplayServer::get_singleton()->window_get_popup_safe_rect(get_window_id());
+ Rect2 safe_area = get_embedder()->subwindow_get_popup_safe_rect(this);
Point2 pos = DisplayServer::get_singleton()->mouse_get_position() - mouse_pos_adjusted;
if (safe_area == Rect2i() || !safe_area.has_point(pos)) {
Popup::_parent_focused();
@@ -1963,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;
}
@@ -1973,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);
}
}
@@ -2303,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 8fb2024e9c..db7196fb07 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"
@@ -1097,6 +1098,11 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + gloff.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
+ } else if (item_fx->type == ITEM_PULSE) {
+ ItemPulse *item_pulse = static_cast<ItemPulse *>(item_fx);
+
+ const float sined_time = (Math::ease(Math::pingpong(item_pulse->elapsed_time, 1.0 / item_pulse->frequency) * item_pulse->frequency, item_pulse->ease));
+ font_color = font_color.lerp(font_color * item_pulse->color, sined_time);
}
}
@@ -1315,6 +1321,11 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + off.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
+ } else if (item_fx->type == ITEM_PULSE) {
+ ItemPulse *item_pulse = static_cast<ItemPulse *>(item_fx);
+
+ const float sined_time = (Math::ease(Math::pingpong(item_pulse->elapsed_time, 1.0 / item_pulse->frequency) * item_pulse->frequency, item_pulse->ease));
+ font_color = font_color.lerp(font_color * item_pulse->color, sined_time);
}
}
@@ -1675,7 +1686,7 @@ void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, double p_delta
while (it) {
ItemFX *ifx = nullptr;
- if (it->type == ITEM_CUSTOMFX || it->type == ITEM_SHAKE || it->type == ITEM_WAVE || it->type == ITEM_TORNADO || it->type == ITEM_RAINBOW) {
+ if (it->type == ITEM_CUSTOMFX || it->type == ITEM_SHAKE || it->type == ITEM_WAVE || it->type == ITEM_TORNADO || it->type == ITEM_RAINBOW || it->type == ITEM_PULSE) {
ifx = static_cast<ItemFX *>(it);
}
@@ -2616,7 +2627,7 @@ bool RichTextLabel::_find_strikethrough(Item *p_item) {
void RichTextLabel::_fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack) {
Item *item = p_item;
while (item) {
- if (item->type == ITEM_CUSTOMFX || item->type == ITEM_SHAKE || item->type == ITEM_WAVE || item->type == ITEM_TORNADO || item->type == ITEM_RAINBOW) {
+ if (item->type == ITEM_CUSTOMFX || item->type == ITEM_SHAKE || item->type == ITEM_WAVE || item->type == ITEM_TORNADO || item->type == ITEM_RAINBOW || item->type == ITEM_PULSE) {
r_stack.push_back(static_cast<ItemFX *>(item));
}
@@ -3444,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;
@@ -3454,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;
@@ -3465,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;
@@ -3476,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;
@@ -3487,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;
@@ -3494,6 +3510,17 @@ void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_freq
_add_item(item, true);
}
+void RichTextLabel::push_pulse(const Color &p_color, float p_frequency, float p_ease) {
+ _stop_thread();
+ MutexLock data_lock(data_mutex);
+
+ ItemPulse *item = memnew(ItemPulse);
+ item->color = p_color;
+ item->frequency = p_frequency;
+ item->ease = p_ease;
+ _add_item(item, true);
+}
+
void RichTextLabel::push_bgcolor(const Color &p_color) {
_stop_thread();
MutexLock data_lock(data_mutex);
@@ -3520,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;
@@ -3528,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);
@@ -3621,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);
@@ -4677,7 +4739,29 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front("rainbow");
set_process_internal(true);
+ } else if (bbcode_name == "pulse") {
+ Color color = Color(1, 1, 1, 0.25);
+ OptionMap::Iterator color_option = bbcode_options.find("color");
+ if (color_option) {
+ color = Color::from_string(color_option->value, color);
+ }
+
+ float frequency = 1.0;
+ OptionMap::Iterator freq_option = bbcode_options.find("freq");
+ if (freq_option) {
+ frequency = freq_option->value.to_float();
+ }
+
+ float ease = -2.0;
+ OptionMap::Iterator ease_option = bbcode_options.find("ease");
+ if (ease_option) {
+ ease = ease_option->value.to_float();
+ }
+ push_pulse(color, frequency, ease);
+ pos = brk_end + 1;
+ tag_stack.push_front("pulse");
+ set_process_internal(true);
} else if (tag.begins_with("bgcolor=")) {
String color_str = tag.substr(8, tag.length()).unquote();
Color color = Color::from_string(color_str, theme_cache.default_color);
@@ -5480,7 +5564,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2(0, 0, 0, 0)));
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
ClassDB::bind_method(D_METHOD("remove_paragraph", "paragraph"), &RichTextLabel::remove_paragraph);
- ClassDB::bind_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::push_font);
+ ClassDB::bind_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::push_font, DEFVAL(0));
ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size);
ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal);
ClassDB::bind_method(D_METHOD("push_bold"), &RichTextLabel::push_bold);
@@ -5499,7 +5583,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough);
ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align", "align_to_row"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGNMENT_TOP), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0)));
- ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand);
+ ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand, DEFVAL(1));
ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color);
ClassDB::bind_method(D_METHOD("set_cell_border_color", "color"), &RichTextLabel::set_cell_border_color);
ClassDB::bind_method(D_METHOD("set_cell_size_override", "min_size", "max_size"), &RichTextLabel::set_cell_size_override);
@@ -5508,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);
@@ -5620,6 +5707,11 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("_thread_end"), &RichTextLabel::_thread_end);
+#ifndef DISABLE_DEPRECATED
+ ClassDB::bind_compatibility_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::push_font);
+ ClassDB::bind_compatibility_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand);
+#endif // DISABLE_DEPRECATED
+
// Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets.
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
@@ -5720,10 +5812,12 @@ int RichTextLabel::get_character_line(int p_char) {
int to_line = main->first_invalid_line.load();
for (int i = 0; i < to_line; i++) {
MutexLock lock(main->lines[i].text_buf->get_mutex());
- if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) {
+ int char_offset = main->lines[i].char_offset;
+ int char_count = main->lines[i].char_count;
+ if (char_offset <= p_char && p_char < char_offset + char_count) {
for (int j = 0; j < main->lines[i].text_buf->get_line_count(); j++) {
Vector2i range = main->lines[i].text_buf->get_line_range(j);
- if (main->lines[i].char_offset + range.x < p_char && p_char <= main->lines[i].char_offset + range.y) {
+ if (char_offset + range.x <= p_char && p_char <= char_offset + range.y) {
return line_count;
}
line_count++;
@@ -5738,13 +5832,11 @@ int RichTextLabel::get_character_line(int p_char) {
int RichTextLabel::get_character_paragraph(int p_char) {
_validate_line_caches();
- int para_count = 0;
int to_line = main->first_invalid_line.load();
for (int i = 0; i < to_line; i++) {
- if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) {
- return para_count;
- } else {
- para_count++;
+ int char_offset = main->lines[i].char_offset;
+ if (char_offset <= p_char && p_char < char_offset + main->lines[i].char_count) {
+ return i;
}
}
return -1;
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 66891ab567..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"
@@ -70,12 +70,14 @@ public:
ITEM_WAVE,
ITEM_TORNADO,
ITEM_RAINBOW,
+ ITEM_PULSE,
ITEM_BGCOLOR,
ITEM_FGCOLOR,
ITEM_META,
ITEM_HINT,
ITEM_DROPCAP,
- ITEM_CUSTOMFX
+ ITEM_CUSTOMFX,
+ ITEM_CONTEXT
};
enum MenuItems {
@@ -343,6 +345,14 @@ private:
ItemRainbow() { type = ITEM_RAINBOW; }
};
+ struct ItemPulse : public ItemFX {
+ Color color = Color(1.0, 1.0, 1.0, 0.25);
+ float frequency = 1.0f;
+ float ease = -2.0f;
+
+ ItemPulse() { type = ITEM_PULSE; }
+ };
+
struct ItemBGColor : public Item {
Color color;
ItemBGColor() { type = ITEM_BGCOLOR; }
@@ -370,6 +380,10 @@ private:
}
};
+ struct ItemContext : public Item {
+ ItemContext() { type = ITEM_CONTEXT; }
+ };
+
ItemFrame *main = nullptr;
Item *current = nullptr;
ItemFrame *current_frame = nullptr;
@@ -611,9 +625,11 @@ public:
void push_wave(float p_frequency, float p_amplitude, bool p_connected);
void push_tornado(float p_frequency, float p_radius, bool p_connected);
void push_rainbow(float p_saturation, float p_value, float p_frequency);
+ void push_pulse(const Color &p_color, float p_frequency, float p_ease);
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);
@@ -622,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/slider.cpp b/scene/gui/slider.cpp
index e2fb7efb19..398f637e85 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -256,7 +256,7 @@ void Slider::_notification(int p_what) {
Ref<StyleBox> style = theme_cache.slider_style;
Ref<Texture2D> tick = theme_cache.tick_icon;
- bool highlighted = mouse_inside || has_focus();
+ bool highlighted = editable && (mouse_inside || has_focus());
Ref<Texture2D> grabber;
if (editable) {
if (highlighted) {
diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index 9105837486..851a94b32f 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -147,12 +147,16 @@ void SubViewportContainer::_notification(int p_what) {
}
} break;
- case NOTIFICATION_MOUSE_ENTER: {
- _notify_viewports(NOTIFICATION_VP_MOUSE_ENTER);
+ case NOTIFICATION_FOCUS_ENTER: {
+ // If focused, send InputEvent to the SubViewport before the Gui-Input stage.
+ set_process_input(true);
+ set_process_unhandled_input(false);
} break;
- case NOTIFICATION_MOUSE_EXIT: {
- _notify_viewports(NOTIFICATION_VP_MOUSE_EXIT);
+ case NOTIFICATION_FOCUS_EXIT: {
+ // A different Control has focus and should receive Gui-Input before the InputEvent is sent to the SubViewport.
+ set_process_input(false);
+ set_process_unhandled_input(true);
} break;
}
}
@@ -168,6 +172,14 @@ void SubViewportContainer::_notify_viewports(int p_notification) {
}
void SubViewportContainer::input(const Ref<InputEvent> &p_event) {
+ _propagate_nonpositional_event(p_event);
+}
+
+void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) {
+ _propagate_nonpositional_event(p_event);
+}
+
+void SubViewportContainer::_propagate_nonpositional_event(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
if (Engine::get_singleton()->is_editor_hint()) {
@@ -247,6 +259,10 @@ PackedStringArray SubViewportContainer::get_configuration_warnings() const {
warnings.push_back(RTR("This node doesn't have a SubViewport as child, so it can't display its intended content.\nConsider adding a SubViewport as a child to provide something displayable."));
}
+ if (get_default_cursor_shape() != Control::CURSOR_ARROW) {
+ warnings.push_back(RTR("The default mouse cursor shape of SubViewportContainer has no effect.\nConsider leaving it at its initial value `CURSOR_ARROW`."));
+ }
+
return warnings;
}
@@ -262,5 +278,6 @@ void SubViewportContainer::_bind_methods() {
}
SubViewportContainer::SubViewportContainer() {
- set_process_input(true);
+ set_process_unhandled_input(true);
+ set_focus_mode(FOCUS_CLICK);
}
diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h
index 8e5f5d157d..3c6cd09d66 100644
--- a/scene/gui/subviewport_container.h
+++ b/scene/gui/subviewport_container.h
@@ -41,6 +41,7 @@ class SubViewportContainer : public Container {
void _notify_viewports(int p_notification);
bool _is_propagated_in_gui_input(const Ref<InputEvent> &p_event);
void _send_event_to_viewports(const Ref<InputEvent> &p_event);
+ void _propagate_nonpositional_event(const Ref<InputEvent> &p_event);
protected:
void _notification(int p_what);
@@ -54,6 +55,7 @@ public:
bool is_stretch_enabled() const;
virtual void input(const Ref<InputEvent> &p_event) override;
+ virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
virtual void gui_input(const Ref<InputEvent> &p_event) override;
void set_stretch_shrink(int p_shrink);
int get_stretch_shrink() const;
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 35564eb458..959a51eff9 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -881,6 +881,8 @@ void TabBar::_update_hover() {
if (hover != -1) {
emit_signal(SNAME("tab_hovered"), hover);
}
+
+ _update_cache();
queue_redraw();
}
@@ -988,6 +990,7 @@ void TabBar::_on_mouse_exited() {
highlight_arrow = -1;
dragging_valid_tab = false;
+ _update_cache();
queue_redraw();
}
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/tree.cpp b/scene/gui/tree.cpp
index f9f7438576..7038145c80 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -247,29 +247,30 @@ void TreeItem::_propagate_check_through_parents(int p_column, bool p_emit_signal
return;
}
- bool all_unchecked_and_not_indeterminate = true;
- bool any_unchecked_or_indeterminate = false;
+ bool any_checked = false;
+ bool any_unchecked = false;
+ bool any_indeterminate = false;
TreeItem *child_item = current->get_first_child();
while (child_item) {
if (!child_item->is_checked(p_column)) {
- any_unchecked_or_indeterminate = true;
+ any_unchecked = true;
if (child_item->is_indeterminate(p_column)) {
- all_unchecked_and_not_indeterminate = false;
+ any_indeterminate = true;
break;
}
} else {
- all_unchecked_and_not_indeterminate = false;
+ any_checked = true;
}
child_item = child_item->get_next();
}
- if (all_unchecked_and_not_indeterminate) {
- current->set_checked(p_column, false);
- } else if (any_unchecked_or_indeterminate) {
+ if (any_indeterminate || (any_checked && any_unchecked)) {
current->set_indeterminate(p_column, true);
+ } else if (current->is_indeterminate(p_column) && !any_checked) {
+ current->set_indeterminate(p_column, false);
} else {
- current->set_checked(p_column, true);
+ current->set_checked(p_column, any_checked);
}
if (p_emit_signal) {
@@ -1176,6 +1177,12 @@ int TreeItem::get_button_by_id(int p_column, int p_id) const {
return -1;
}
+void TreeItem::set_button_tooltip_text(int p_column, int p_index, const String &p_tooltip) {
+ ERR_FAIL_INDEX(p_column, cells.size());
+ ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size());
+ cells.write[p_column].buttons.write[p_index].tooltip = p_tooltip;
+}
+
void TreeItem::set_button(int p_column, int p_index, const Ref<Texture2D> &p_button) {
ERR_FAIL_COND(p_button.is_null());
ERR_FAIL_INDEX(p_column, cells.size());
@@ -1586,6 +1593,7 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_button_id", "column", "button_index"), &TreeItem::get_button_id);
ClassDB::bind_method(D_METHOD("get_button_by_id", "column", "id"), &TreeItem::get_button_by_id);
ClassDB::bind_method(D_METHOD("get_button", "column", "button_index"), &TreeItem::get_button);
+ ClassDB::bind_method(D_METHOD("set_button_tooltip_text", "column", "button_index", "tooltip"), &TreeItem::set_button_tooltip_text);
ClassDB::bind_method(D_METHOD("set_button", "column", "button_index", "button"), &TreeItem::set_button);
ClassDB::bind_method(D_METHOD("erase_button", "column", "button_index"), &TreeItem::erase_button);
ClassDB::bind_method(D_METHOD("set_button_disabled", "column", "button_index", "disabled"), &TreeItem::set_button_disabled);
@@ -1704,6 +1712,10 @@ void Tree::_update_theme_item_cache() {
theme_cache.drop_position_color = get_theme_color(SNAME("drop_position_color"));
theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
theme_cache.v_separation = get_theme_constant(SNAME("v_separation"));
+ theme_cache.inner_item_margin_bottom = get_theme_constant(SNAME("inner_item_margin_bottom"));
+ theme_cache.inner_item_margin_left = get_theme_constant(SNAME("inner_item_margin_left"));
+ theme_cache.inner_item_margin_right = get_theme_constant(SNAME("inner_item_margin_right"));
+ theme_cache.inner_item_margin_top = get_theme_constant(SNAME("inner_item_margin_top"));
theme_cache.item_margin = get_theme_constant(SNAME("item_margin"));
theme_cache.button_margin = get_theme_constant(SNAME("button_margin"));
theme_cache.icon_max_width = get_theme_constant(SNAME("icon_max_width"));
@@ -1841,13 +1853,14 @@ int Tree::get_item_height(TreeItem *p_item) const {
void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color) {
ERR_FAIL_COND(theme_cache.font.is_null());
- Rect2i rect = p_rect;
+ Rect2i rect = p_rect.grow_individual(-theme_cache.inner_item_margin_left, -theme_cache.inner_item_margin_top, -theme_cache.inner_item_margin_right, -theme_cache.inner_item_margin_bottom);
Size2 ts = p_cell.text_buf->get_size();
bool rtl = is_layout_rtl();
int w = 0;
+ Size2i bmsize;
if (!p_cell.icon.is_null()) {
- Size2i bmsize = _get_cell_icon_size(p_cell);
+ bmsize = _get_cell_icon_size(p_cell);
w += bmsize.width + theme_cache.h_separation;
if (rect.size.width > 0 && (w + ts.width) > rect.size.width) {
ts.width = rect.size.width - w;
@@ -1886,8 +1899,6 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
}
if (!p_cell.icon.is_null()) {
- Size2i bmsize = _get_cell_icon_size(p_cell);
-
p_cell.draw_icon(ci, rect.position + Size2i(0, Math::floor((real_t)(rect.size.y - bmsize.y) / 2)), bmsize, p_icon_color);
rect.position.x += bmsize.x + theme_cache.h_separation;
rect.size.x -= bmsize.x + theme_cache.h_separation;
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index c0814c651d..ce4379d7fe 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -262,6 +262,7 @@ public:
int get_button_id(int p_column, int p_index) const;
void erase_button(int p_column, int p_index);
int get_button_by_id(int p_column, int p_id) const;
+ void set_button_tooltip_text(int p_column, int p_index, const String &p_tooltip);
void set_button(int p_column, int p_index, const Ref<Texture2D> &p_button);
void set_button_color(int p_column, int p_index, const Color &p_color);
void set_button_disabled(int p_column, int p_index, bool p_disabled);
@@ -550,6 +551,10 @@ private:
int h_separation = 0;
int v_separation = 0;
+ int inner_item_margin_bottom = 0;
+ int inner_item_margin_left = 0;
+ int inner_item_margin_right = 0;
+ int inner_item_margin_top = 0;
int item_margin = 0;
int button_margin = 0;
int icon_max_width = 0;
diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp
index 19a54e01c4..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"
@@ -159,6 +160,10 @@ void VideoStreamPlayer::_notification(int p_notification) {
playback->update(delta); // playback->is_playing() returns false in the last video frame
if (!playback->is_playing()) {
+ if (loop) {
+ play();
+ return;
+ }
emit_signal(SceneStringNames::get_singleton()->finished);
}
} break;
@@ -221,6 +226,14 @@ bool VideoStreamPlayer::has_expand() const {
return expand;
}
+void VideoStreamPlayer::set_loop(bool p_loop) {
+ loop = p_loop;
+}
+
+bool VideoStreamPlayer::has_loop() const {
+ return loop;
+}
+
void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
stop();
@@ -389,6 +402,13 @@ String VideoStreamPlayer::get_stream_name() const {
return stream->get_name();
}
+double VideoStreamPlayer::get_stream_length() const {
+ if (playback.is_null()) {
+ return 0;
+ }
+ return playback->get_length();
+}
+
double VideoStreamPlayer::get_stream_position() const {
if (playback.is_null()) {
return 0;
@@ -461,6 +481,9 @@ void VideoStreamPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_paused", "paused"), &VideoStreamPlayer::set_paused);
ClassDB::bind_method(D_METHOD("is_paused"), &VideoStreamPlayer::is_paused);
+ ClassDB::bind_method(D_METHOD("set_loop", "loop"), &VideoStreamPlayer::set_loop);
+ ClassDB::bind_method(D_METHOD("has_loop"), &VideoStreamPlayer::has_loop);
+
ClassDB::bind_method(D_METHOD("set_volume", "volume"), &VideoStreamPlayer::set_volume);
ClassDB::bind_method(D_METHOD("get_volume"), &VideoStreamPlayer::get_volume);
@@ -471,6 +494,7 @@ void VideoStreamPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_audio_track"), &VideoStreamPlayer::get_audio_track);
ClassDB::bind_method(D_METHOD("get_stream_name"), &VideoStreamPlayer::get_stream_name);
+ ClassDB::bind_method(D_METHOD("get_stream_length"), &VideoStreamPlayer::get_stream_length);
ClassDB::bind_method(D_METHOD("set_stream_position", "position"), &VideoStreamPlayer::set_stream_position);
ClassDB::bind_method(D_METHOD("get_stream_position"), &VideoStreamPlayer::get_stream_position);
@@ -498,6 +522,7 @@ void VideoStreamPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "has_autoplay");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_paused", "is_paused");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::INT, "buffering_msec", PROPERTY_HINT_RANGE, "10,1000,suffix:ms"), "set_buffering_msec", "get_buffering_msec");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "stream_position", PROPERTY_HINT_RANGE, "0,1280000,0.1", PROPERTY_USAGE_NONE), "set_stream_position", "get_stream_position");
diff --git a/scene/gui/video_stream_player.h b/scene/gui/video_stream_player.h
index 1fd599a9e1..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);
@@ -65,6 +67,7 @@ class VideoStreamPlayer : public Control {
float volume = 1.0;
double last_audio_time = 0.0;
bool expand = false;
+ bool loop = false;
int buffering_ms = 500;
int audio_track = 0;
int bus_index = 0;
@@ -94,6 +97,9 @@ public:
void stop();
bool is_playing() const;
+ void set_loop(bool p_loop);
+ bool has_loop() const;
+
void set_paused(bool p_paused);
bool is_paused() const;
@@ -104,6 +110,7 @@ public:
float get_volume_db() const;
String get_stream_name() const;
+ double get_stream_length() const;
double get_stream_position() const;
void set_stream_position(double p_position);
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index fcb951a89d..7f6bbb17c3 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -170,6 +170,7 @@ protected:
_FORCE_INLINE_ void set_hide_clip_children(bool p_value) { hide_clip_children = p_value; }
GDVIRTUAL0(_draw)
+
public:
enum {
NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED, //unique
@@ -179,7 +180,6 @@ public:
NOTIFICATION_EXIT_CANVAS = 33,
NOTIFICATION_LOCAL_TRANSFORM_CHANGED = 35,
NOTIFICATION_WORLD_2D_CHANGED = 36,
-
};
/* EDITOR */
@@ -189,7 +189,7 @@ public:
// Save and restore a CanvasItem state
virtual void _edit_set_state(const Dictionary &p_state) {}
- virtual Dictionary _edit_get_state() const { return Dictionary(); };
+ virtual Dictionary _edit_get_state() const { return Dictionary(); }
// Used to move the node
virtual void _edit_set_position(const Point2 &p_position) = 0;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 3715c06a33..8d5133311a 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -645,7 +645,8 @@ int Node::get_multiplayer_authority() const {
bool Node::is_multiplayer_authority() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
- return get_multiplayer()->get_unique_id() == data.multiplayer_authority;
+ Ref<MultiplayerAPI> api = get_multiplayer();
+ return api.is_valid() && (api->get_unique_id() == data.multiplayer_authority);
}
/***** RPC CONFIG ********/
@@ -724,7 +725,12 @@ Error Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallE
Error Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
- return get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
+
+ Ref<MultiplayerAPI> api = get_multiplayer();
+ if (api.is_null()) {
+ return ERR_UNCONFIGURED;
+ }
+ return api->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
@@ -1139,7 +1145,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 +1156,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) {
@@ -1906,7 +1911,7 @@ void Node::set_owner(Node *p_owner) {
check = check->data.parent;
}
- ERR_FAIL_COND(!owner_valid);
+ ERR_FAIL_COND_MSG(!owner_valid, "Invalid owner. Owner must be an ancestor in the tree.");
_set_owner_nocheck(p_owner);
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 9a7f34d277..db9c1efa68 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"
@@ -444,9 +445,8 @@ void SceneTree::set_group(const StringName &p_group, const String &p_name, const
void SceneTree::initialize() {
ERR_FAIL_NULL(root);
- initialized = true;
- root->_set_tree(this);
MainLoop::initialize();
+ root->_set_tree(this);
}
bool SceneTree::physics_process(double p_time) {
@@ -550,6 +550,10 @@ bool SceneTree::process(double p_time) {
#endif // _3D_DISABLED
#endif // TOOLS_ENABLED
+ if (unlikely(pending_new_scene)) {
+ _flush_scene_change();
+ }
+
return _quit;
}
@@ -618,20 +622,18 @@ void SceneTree::finalize() {
_flush_ugc();
- initialized = false;
-
- MainLoop::finalize();
-
if (root) {
root->_set_tree(nullptr);
root->_propagate_after_exit_tree();
memdelete(root); //delete root
root = nullptr;
+
+ // In case deletion of some objects was queued when destructing the `root`.
+ // E.g. if `queue_free()` was called for some node outside the tree when handling NOTIFICATION_PREDELETE for some node in the tree.
+ _flush_delete_queue();
}
- // In case deletion of some objects was queued when destructing the `root`.
- // E.g. if `queue_free()` was called for some node outside the tree when handling NOTIFICATION_PREDELETE for some node in the tree.
- _flush_delete_queue();
+ MainLoop::finalize();
// Cleanup timers.
for (Ref<SceneTreeTimer> &timer : timers) {
@@ -1378,27 +1380,16 @@ Node *SceneTree::get_current_scene() const {
return current_scene;
}
-void SceneTree::_change_scene(Node *p_to) {
- ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Changing scene can only be done from the main thread.");
- if (current_scene) {
- memdelete(current_scene);
- current_scene = nullptr;
- }
-
- // If we're quitting, abort.
- if (unlikely(_quit)) {
- if (p_to) { // Prevent memory leak.
- memdelete(p_to);
- }
- return;
- }
-
- if (p_to) {
- current_scene = p_to;
- root->add_child(p_to);
- // Update display for cursor instantly.
- root->update_mouse_cursor_state();
+void SceneTree::_flush_scene_change() {
+ if (prev_scene) {
+ memdelete(prev_scene);
+ prev_scene = nullptr;
}
+ current_scene = pending_new_scene;
+ root->add_child(pending_new_scene);
+ pending_new_scene = nullptr;
+ // Update display for cursor instantly.
+ root->update_mouse_cursor_state();
}
Error SceneTree::change_scene_to_file(const String &p_path) {
@@ -1417,7 +1408,22 @@ Error SceneTree::change_scene_to_packed(const Ref<PackedScene> &p_scene) {
Node *new_scene = p_scene->instantiate();
ERR_FAIL_NULL_V(new_scene, ERR_CANT_CREATE);
- call_deferred(SNAME("_change_scene"), new_scene);
+ // If called again while a change is pending.
+ if (pending_new_scene) {
+ queue_delete(pending_new_scene);
+ pending_new_scene = nullptr;
+ }
+
+ prev_scene = current_scene;
+
+ if (current_scene) {
+ // Let as many side effects as possible happen or be queued now,
+ // so they are run before the scene is actually deleted.
+ root->remove_child(current_scene);
+ }
+ DEV_ASSERT(!current_scene);
+
+ pending_new_scene = new_scene;
return OK;
}
@@ -1595,8 +1601,6 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("reload_current_scene"), &SceneTree::reload_current_scene);
ClassDB::bind_method(D_METHOD("unload_current_scene"), &SceneTree::unload_current_scene);
- ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene);
-
ClassDB::bind_method(D_METHOD("set_multiplayer", "multiplayer", "root_path"), &SceneTree::set_multiplayer, DEFVAL(NodePath()));
ClassDB::bind_method(D_METHOD("get_multiplayer", "for_path"), &SceneTree::get_multiplayer, DEFVAL(NodePath()));
ClassDB::bind_method(D_METHOD("set_multiplayer_poll_enabled", "enabled"), &SceneTree::set_multiplayer_poll_enabled);
@@ -1835,6 +1839,14 @@ SceneTree::SceneTree() {
}
SceneTree::~SceneTree() {
+ if (prev_scene) {
+ memdelete(prev_scene);
+ prev_scene = nullptr;
+ }
+ if (pending_new_scene) {
+ memdelete(pending_new_scene);
+ pending_new_scene = nullptr;
+ }
if (root) {
root->_set_tree(nullptr);
root->_propagate_after_exit_tree();
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 452fac7423..e1597d3890 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -138,7 +138,6 @@ private:
HashMap<StringName, Group> group_map;
bool _quit = false;
- bool initialized = false;
StringName tree_changed_name = "tree_changed";
StringName node_added_name = "node_added";
@@ -179,6 +178,8 @@ private:
TypedArray<Node> _get_nodes_in_group(const StringName &p_group);
Node *current_scene = nullptr;
+ Node *prev_scene = nullptr;
+ Node *pending_new_scene = nullptr;
Color debug_collisions_color;
Color debug_collision_contact_color;
@@ -189,7 +190,7 @@ private:
Ref<Material> collision_material;
int collision_debug_contacts;
- void _change_scene(Node *p_to);
+ void _flush_scene_change();
List<Ref<SceneTreeTimer>> timers;
List<Ref<Tween>> tweens;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 8609b77944..94e5c07552 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.
@@ -422,7 +421,12 @@ void Viewport::_sub_window_remove(Window *p_window) {
ERR_FAIL_NULL(RenderingServer::get_singleton());
- RS::get_singleton()->free(gui.sub_windows[index].canvas_item);
+ SubWindow sw = gui.sub_windows[index];
+ if (gui.subwindow_over == sw.window) {
+ sw.window->_mouse_leave_viewport();
+ gui.subwindow_over = nullptr;
+ }
+ RS::get_singleton()->free(sw.canvas_item);
gui.sub_windows.remove_at(index);
if (gui.sub_windows.size() == 0) {
@@ -464,7 +468,7 @@ void Viewport::_sub_window_remove(Window *p_window) {
RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, p_window->parent ? p_window->parent->viewport : RID());
}
-int Viewport::_sub_window_find(Window *p_window) {
+int Viewport::_sub_window_find(Window *p_window) const {
for (int i = 0; i < gui.sub_windows.size(); i++) {
if (gui.sub_windows[i].window == p_window) {
return i;
@@ -634,10 +638,8 @@ void Viewport::_notification(int p_what) {
case NOTIFICATION_VP_MOUSE_EXIT: {
gui.mouse_in_viewport = false;
_drop_physics_mouseover();
- _drop_mouse_over();
- _gui_cancel_tooltip();
- // When the mouse exits the viewport, we want to end mouse_over, but
- // not mouse_focus, because, for example, we want to continue
+ // When the mouse exits the viewport, we don't want to end
+ // mouse_focus, because, for example, we want to continue
// dragging a scrollbar even if the mouse has left the viewport.
} break;
@@ -689,17 +691,26 @@ void Viewport::_process_picking() {
PhysicsDirectSpaceState2D *ss2d = PhysicsServer2D::get_singleton()->space_get_direct_state(find_world_2d()->get_space());
- bool has_mouse_event = false;
- for (const Ref<InputEvent> &e : physics_picking_events) {
- Ref<InputEventMouse> m = e;
- if (m.is_valid()) {
- has_mouse_event = true;
- break;
+ SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
+ bool parent_ignore_mouse = (parent_svc && parent_svc->get_mouse_filter() == Control::MOUSE_FILTER_IGNORE);
+ bool create_passive_hover_event = true;
+ if (gui.mouse_over || parent_ignore_mouse) {
+ // When the mouse is over a Control node, passive hovering would cause input events for Colliders, that are behind Control nodes.
+ // When parent SubViewportContainer ignores mouse, that setting should be respected.
+ create_passive_hover_event = false;
+ } else {
+ for (const Ref<InputEvent> &e : physics_picking_events) {
+ Ref<InputEventMouse> m = e;
+ if (m.is_valid()) {
+ // A mouse event exists, so passive hovering isn't necessary.
+ create_passive_hover_event = false;
+ break;
+ }
}
}
- if (!has_mouse_event) {
- // If no mouse event exists, create a motion one. This is necessary because objects or camera may have moved.
+ if (create_passive_hover_event) {
+ // Create a mouse motion event. This is necessary because objects or camera may have moved.
// While this extra event is sent, it is checked if both camera and last object and last ID did not move.
// If nothing changed, the event is discarded to avoid flooding with unnecessary motion events every frame.
Ref<InputEventMouseMotion> mm;
@@ -717,6 +728,7 @@ void Viewport::_process_picking() {
}
while (physics_picking_events.size()) {
+ local_input_handled = false;
Ref<InputEvent> ev = physics_picking_events.front()->get();
physics_picking_events.pop_front();
@@ -841,6 +853,9 @@ void Viewport::_process_picking() {
capture_object = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_capture));
if (!capture_object || !camera_3d || (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed())) {
physics_object_capture = ObjectID();
+ } else {
+ last_id = physics_object_capture;
+ last_object = capture_object;
}
}
@@ -1344,7 +1359,12 @@ Vector2 Viewport::get_mouse_position() const {
// In this case get_screen_transform is not applicable, because it is ambiguous.
return gui.last_mouse_pos;
} else if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_MOUSE)) {
- return get_screen_transform_internal(true).affine_inverse().xform(DisplayServer::get_singleton()->mouse_get_position());
+ Transform2D xform = get_screen_transform_internal(true);
+ if (xform.determinant() == 0) {
+ // Screen transform can be non-invertible when the Window is minimized.
+ return Vector2();
+ }
+ return xform.affine_inverse().xform(DisplayServer::get_singleton()->mouse_get_position());
} else {
// Fallback to Input for getting mouse position in case of emulated mouse.
return get_screen_transform_internal().affine_inverse().xform(Input::get_singleton()->get_mouse_position());
@@ -1720,11 +1740,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.last_mouse_focus = gui.mouse_focus;
if (!gui.mouse_focus) {
- gui.mouse_focus_mask.clear();
return;
}
- gui.mouse_focus_mask.clear();
gui.mouse_focus_mask.set_flag(mouse_button_to_mask(mb->get_button_index()));
if (mb->get_button_index() == MouseButton::LEFT) {
@@ -1870,25 +1888,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
Control *over = nullptr;
- if (gui.mouse_in_viewport) {
- over = gui_find_control(mpos);
- }
-
- if (over != gui.mouse_over) {
- if (!gui.mouse_over) {
- _drop_physics_mouseover();
- }
- _drop_mouse_over();
- _gui_cancel_tooltip();
-
- if (over) {
- _gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER);
- gui.mouse_over = over;
- }
- }
-
if (gui.mouse_focus) {
over = gui.mouse_focus;
+ } else if (gui.mouse_in_viewport) {
+ over = gui_find_control(mpos);
}
DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)Input::get_singleton()->get_default_cursor_shape();
@@ -2074,7 +2077,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE) && !Object::cast_to<SubViewportContainer>(over)) {
DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape);
}
}
@@ -2367,7 +2370,7 @@ void Viewport::_gui_hide_control(Control *p_control) {
gui_release_focus();
}
if (gui.mouse_over == p_control) {
- gui.mouse_over = nullptr;
+ _drop_mouse_over();
}
if (gui.drag_mouse_over == p_control) {
gui.drag_mouse_over = nullptr;
@@ -2390,7 +2393,7 @@ void Viewport::_gui_remove_control(Control *p_control) {
gui.key_focus = nullptr;
}
if (gui.mouse_over == p_control) {
- gui.mouse_over = nullptr;
+ _drop_mouse_over();
}
if (gui.drag_mouse_over == p_control) {
gui.drag_mouse_over = nullptr;
@@ -2444,13 +2447,6 @@ void Viewport::_gui_accept_event() {
}
}
-void Viewport::_drop_mouse_over() {
- if (gui.mouse_over) {
- _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT);
- gui.mouse_over = nullptr;
- }
-}
-
void Viewport::_drop_mouse_focus() {
Control *c = gui.mouse_focus;
BitField<MouseButtonMask> mask = gui.mouse_focus_mask;
@@ -2934,6 +2930,156 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
return true;
}
+void Viewport::_update_mouse_over() {
+ // Update gui.mouse_over and gui.subwindow_over in all Viewports.
+ // Send necessary mouse_enter/mouse_exit signals and the NOTIFICATION_VP_MOUSE_ENTER/NOTIFICATION_VP_MOUSE_EXIT notifications for every Viewport in the SceneTree.
+
+ if (is_attached_in_viewport()) {
+ // Execute this function only, when it is processed by a native Window or a SubViewport, that has no SubViewportContainer as parent.
+ return;
+ }
+
+ if (get_tree()->get_root()->is_embedding_subwindows() || is_sub_viewport()) {
+ // Use embedder logic for calculating mouse position.
+ _update_mouse_over(gui.last_mouse_pos);
+ } else {
+ // Native Window: Use DisplayServer logic for calculating mouse position.
+ Window *receiving_window = get_tree()->get_root()->gui.windowmanager_window_over;
+ if (!receiving_window) {
+ return;
+ }
+
+ Vector2 pos = DisplayServer::get_singleton()->mouse_get_position() - receiving_window->get_position();
+ pos = receiving_window->get_final_transform().affine_inverse().xform(pos);
+
+ receiving_window->_update_mouse_over(pos);
+ }
+}
+
+void Viewport::_update_mouse_over(Vector2 p_pos) {
+ // Look for embedded windows at mouse position.
+ if (is_embedding_subwindows()) {
+ for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
+ Window *sw = gui.sub_windows[i].window;
+ Rect2 swrect = Rect2(sw->get_position(), sw->get_size());
+ Rect2 swrect_border = swrect;
+
+ if (!sw->get_flag(Window::FLAG_BORDERLESS)) {
+ int title_height = sw->get_theme_constant(SNAME("title_height"));
+ int margin = sw->get_theme_constant(SNAME("resize_margin"));
+ swrect_border.position.y -= title_height + margin;
+ swrect_border.size.y += title_height + margin * 2;
+ swrect_border.position.x -= margin;
+ swrect_border.size.x += margin * 2;
+ }
+
+ if (swrect_border.has_point(p_pos)) {
+ if (gui.mouse_over) {
+ _drop_mouse_over();
+ } else if (!gui.subwindow_over) {
+ _drop_physics_mouseover();
+ }
+ if (swrect.has_point(p_pos)) {
+ if (sw != gui.subwindow_over) {
+ if (gui.subwindow_over) {
+ gui.subwindow_over->_mouse_leave_viewport();
+ }
+ gui.subwindow_over = sw;
+ if (!sw->is_input_disabled()) {
+ sw->notification(NOTIFICATION_VP_MOUSE_ENTER);
+ }
+ }
+ if (!sw->is_input_disabled()) {
+ sw->_update_mouse_over(sw->get_final_transform().affine_inverse().xform(p_pos - sw->get_position()));
+ }
+ } else {
+ if (gui.subwindow_over) {
+ gui.subwindow_over->_mouse_leave_viewport();
+ gui.subwindow_over = nullptr;
+ }
+ }
+ return;
+ }
+ }
+
+ if (gui.subwindow_over) {
+ // Take care of moving mouse out of any embedded Window.
+ gui.subwindow_over->_mouse_leave_viewport();
+ gui.subwindow_over = nullptr;
+ }
+ }
+
+ // Look for Controls at mouse position.
+ Control *over = gui_find_control(p_pos);
+ bool notify_embedded_viewports = false;
+ if (over != gui.mouse_over) {
+ if (gui.mouse_over) {
+ _drop_mouse_over();
+ } else {
+ _drop_physics_mouseover();
+ }
+
+ gui.mouse_over = over;
+ if (over) {
+ over->notification(Control::NOTIFICATION_MOUSE_ENTER);
+ notify_embedded_viewports = true;
+ }
+ }
+
+ if (over) {
+ SubViewportContainer *c = Object::cast_to<SubViewportContainer>(over);
+ if (!c) {
+ return;
+ }
+ Vector2 pos = c->get_global_transform_with_canvas().affine_inverse().xform(p_pos);
+ if (c->is_stretch_enabled()) {
+ pos /= c->get_stretch_shrink();
+ }
+
+ for (int i = 0; i < c->get_child_count(); i++) {
+ SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
+ if (!v || v->is_input_disabled()) {
+ continue;
+ }
+ if (notify_embedded_viewports) {
+ v->notification(NOTIFICATION_VP_MOUSE_ENTER);
+ }
+ v->_update_mouse_over(v->get_final_transform().affine_inverse().xform(pos));
+ }
+ }
+}
+
+void Viewport::_mouse_leave_viewport() {
+ if (!is_inside_tree() || is_input_disabled()) {
+ return;
+ }
+ if (gui.subwindow_over) {
+ gui.subwindow_over->_mouse_leave_viewport();
+ gui.subwindow_over = nullptr;
+ } else if (gui.mouse_over) {
+ _drop_mouse_over();
+ }
+ notification(NOTIFICATION_VP_MOUSE_EXIT);
+}
+
+void Viewport::_drop_mouse_over() {
+ _gui_cancel_tooltip();
+ SubViewportContainer *c = Object::cast_to<SubViewportContainer>(gui.mouse_over);
+ if (c) {
+ for (int i = 0; i < c->get_child_count(); i++) {
+ SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
+ if (!v) {
+ continue;
+ }
+ v->_mouse_leave_viewport();
+ }
+ }
+ if (gui.mouse_over->is_inside_tree()) {
+ gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT);
+ }
+ gui.mouse_over = nullptr;
+}
+
void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
ERR_MAIN_THREAD_GUARD;
ERR_FAIL_COND(!is_inside_tree());
@@ -2959,6 +3105,8 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
Ref<InputEventMouse> me = ev;
if (me.is_valid()) {
gui.last_mouse_pos = me->get_position();
+
+ _update_mouse_over();
}
if (is_embedding_subwindows() && _sub_windows_forward_input(ev)) {
@@ -3025,18 +3173,18 @@ void Viewport::_push_unhandled_input_internal(const Ref<InputEvent> &p_event) {
get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, p_event, this);
}
- // Unhandled Input.
- if (!is_input_handled()) {
- ERR_FAIL_COND(!is_inside_tree());
- get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, p_event, this);
- }
-
// Unhandled key Input - Used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, and to handle Unicode input with Alt / Ctrl modifiers after handling shortcuts.
if (!is_input_handled() && (Object::cast_to<InputEventKey>(*p_event) != nullptr)) {
ERR_FAIL_COND(!is_inside_tree());
get_tree()->_call_input_pause(unhandled_key_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, p_event, this);
}
+ // Unhandled Input.
+ if (!is_input_handled()) {
+ ERR_FAIL_COND(!is_inside_tree());
+ get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, p_event, this);
+ }
+
if (physics_object_picking && !is_input_handled()) {
if (Input::get_singleton()->get_mouse_mode() != Input::MOUSE_MODE_CAPTURED &&
(Object::cast_to<InputEventMouse>(*p_event) ||
@@ -3096,7 +3244,7 @@ void Viewport::set_disable_input(bool p_disable) {
}
if (p_disable) {
_drop_mouse_focus();
- _drop_mouse_over();
+ _mouse_leave_viewport();
_gui_cancel_tooltip();
}
disable_input = p_disable;
@@ -3485,6 +3633,26 @@ bool Viewport::is_embedding_subwindows() const {
return gui.embed_subwindows_hint;
}
+void Viewport::subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect) {
+ int index = _sub_window_find(p_window);
+ ERR_FAIL_COND(index == -1);
+
+ SubWindow sw = gui.sub_windows[index];
+ sw.parent_safe_rect = p_rect;
+}
+
+Rect2i Viewport::subwindow_get_popup_safe_rect(Window *p_window) const {
+ int index = _sub_window_find(p_window);
+ // FIXME: Re-enable ERR_FAIL_COND after rewriting embedded window popup closing.
+ // Currently it is expected, that index == -1 can happen.
+ if (index == -1) {
+ return Rect2i();
+ }
+ // ERR_FAIL_COND_V(index == -1, Rect2i());
+
+ return gui.sub_windows[index].parent_safe_rect;
+}
+
void Viewport::pass_mouse_focus_to(Viewport *p_viewport, Control *p_control) {
ERR_MAIN_THREAD_GUARD;
ERR_FAIL_NULL(p_viewport);
@@ -3848,7 +4016,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;
@@ -3856,7 +4024,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));
}
@@ -3907,14 +4075,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));
}
}
@@ -4581,6 +4749,10 @@ bool SubViewport::is_directly_attached_to_screen() const {
return Object::cast_to<SubViewportContainer>(get_parent()) && get_parent()->get_viewport() && get_parent()->get_viewport()->is_directly_attached_to_screen();
}
+bool SubViewport::is_attached_in_viewport() const {
+ return Object::cast_to<SubViewportContainer>(get_parent());
+}
+
void SubViewport::_notification(int p_what) {
ERR_MAIN_THREAD_GUARD;
switch (p_what) {
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index af2907ca6f..7cfad42119 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -338,6 +338,7 @@ private:
struct SubWindow {
Window *window = nullptr;
RID canvas_item;
+ Rect2i parent_safe_rect;
};
// VRS
@@ -345,8 +346,6 @@ private:
Ref<Texture2D> vrs_texture;
struct GUI {
- // info used when this is a window
-
bool forced_mouse_focus = false; //used for menu buttons
bool mouse_in_viewport = true;
bool key_event_accepted = false;
@@ -357,6 +356,8 @@ private:
BitField<MouseButtonMask> mouse_focus_mask;
Control *key_focus = nullptr;
Control *mouse_over = nullptr;
+ Window *subwindow_over = nullptr; // mouse_over and subwindow_over are mutually exclusive. At all times at least one of them is nullptr.
+ Window *windowmanager_window_over = nullptr; // Only used in root Viewport.
Control *drag_mouse_over = nullptr;
Vector2 drag_mouse_over_pos;
Control *tooltip_control = nullptr;
@@ -461,10 +462,13 @@ private:
void _sub_window_update(Window *p_window);
void _sub_window_grab_focus(Window *p_window);
void _sub_window_remove(Window *p_window);
- int _sub_window_find(Window *p_window);
+ int _sub_window_find(Window *p_window) const;
bool _sub_windows_forward_input(const Ref<InputEvent> &p_event);
SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point);
+ void _update_mouse_over();
+ void _update_mouse_over(Vector2 p_pos);
+
virtual bool _can_consume_input_events() const { return true; }
uint64_t event_count = 0;
@@ -477,6 +481,8 @@ protected:
Size2i _get_size_2d_override() const;
bool _is_size_allocated() const;
+ void _mouse_leave_viewport();
+
void _notification(int p_what);
void _process_picking();
static void _bind_methods();
@@ -644,6 +650,8 @@ public:
void set_embedding_subwindows(bool p_embed);
bool is_embedding_subwindows() const;
+ void subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect);
+ Rect2i subwindow_get_popup_safe_rect(Window *p_window) const;
Viewport *get_parent_viewport() const;
Window *get_base_window() const;
@@ -662,6 +670,8 @@ public:
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const;
virtual Transform2D get_popup_base_transform() const { return Transform2D(); }
virtual bool is_directly_attached_to_screen() const { return false; };
+ virtual bool is_attached_in_viewport() const { return false; };
+ virtual bool is_sub_viewport() const { return false; };
#ifndef _3D_DISABLED
bool use_xr = false;
@@ -794,6 +804,8 @@ public:
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
virtual Transform2D get_popup_base_transform() const override;
virtual bool is_directly_attached_to_screen() const override;
+ virtual bool is_attached_in_viewport() const override;
+ virtual bool is_sub_viewport() const override { return true; };
void _validate_property(PropertyInfo &p_property) const;
SubViewport();
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index be88b757fd..bd51f8eeaf 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();
@@ -618,12 +618,6 @@ void Window::_clear_window() {
bool had_focus = has_focus();
- DisplayServer::get_singleton()->window_set_rect_changed_callback(Callable(), window_id);
- DisplayServer::get_singleton()->window_set_window_event_callback(Callable(), window_id);
- DisplayServer::get_singleton()->window_set_input_event_callback(Callable(), window_id);
- DisplayServer::get_singleton()->window_set_input_text_callback(Callable(), window_id);
- DisplayServer::get_singleton()->window_set_drop_files_callback(Callable(), window_id);
-
if (transient_parent && transient_parent->window_id != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_transient(window_id, DisplayServer::INVALID_WINDOW_ID);
}
@@ -677,16 +671,20 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
switch (p_event) {
case DisplayServer::WINDOW_EVENT_MOUSE_ENTER: {
_propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER);
- emit_signal(SNAME("mouse_entered"));
+ Window *root = get_tree()->get_root();
+ DEV_ASSERT(!root->gui.windowmanager_window_over); // Entering a window while a window is hovered should never happen.
+ root->gui.windowmanager_window_over = this;
notification(NOTIFICATION_VP_MOUSE_ENTER);
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape
}
} break;
case DisplayServer::WINDOW_EVENT_MOUSE_EXIT: {
- notification(NOTIFICATION_VP_MOUSE_EXIT);
+ Window *root = get_tree()->get_root();
+ DEV_ASSERT(root->gui.windowmanager_window_over); // Exiting a window, while no window is hovered should never happen.
+ root->gui.windowmanager_window_over->_mouse_leave_viewport();
+ root->gui.windowmanager_window_over = nullptr;
_propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT);
- emit_signal(SNAME("mouse_exited"));
} break;
case DisplayServer::WINDOW_EVENT_FOCUS_IN: {
focused = true;
@@ -1283,6 +1281,14 @@ void Window::_notification(int p_what) {
RS::get_singleton()->viewport_set_active(get_viewport_rid(), false);
} break;
+
+ case NOTIFICATION_VP_MOUSE_ENTER: {
+ emit_signal(SceneStringNames::get_singleton()->mouse_entered);
+ } break;
+
+ case NOTIFICATION_VP_MOUSE_EXIT: {
+ emit_signal(SceneStringNames::get_singleton()->mouse_exited);
+ } break;
}
}
@@ -1823,13 +1829,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 +2171,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 +2184,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 +2197,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 +2226,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 +2236,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 +2246,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);
@@ -2495,6 +2501,10 @@ bool Window::is_directly_attached_to_screen() const {
return is_inside_tree();
}
+bool Window::is_attached_in_viewport() const {
+ return get_embedder();
+}
+
void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title);
@@ -2768,13 +2778,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..24142b8a91 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);
@@ -404,6 +404,7 @@ public:
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
virtual Transform2D get_popup_base_transform() const override;
virtual bool is_directly_attached_to_screen() const override;
+ virtual bool is_attached_in_viewport() const override;
Rect2i get_parent_rect() const;
virtual DisplayServer::WindowID get_window_id() const override;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 9706fb1a5d..4d5d287fbe 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,12 +202,16 @@
#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"
#include "scene/resources/text_line.h"
#include "scene/resources/text_paragraph.h"
#include "scene/resources/texture.h"
+#include "scene/resources/texture_rd.h"
#include "scene/resources/theme.h"
#include "scene/resources/tile_set.h"
#include "scene/resources/video_stream.h"
@@ -662,6 +676,8 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeTexture3DParameter);
GDREGISTER_CLASS(VisualShaderNodeCubemapParameter);
GDREGISTER_CLASS(VisualShaderNodeLinearSceneDepth);
+ GDREGISTER_CLASS(VisualShaderNodeWorldPositionFromDepth);
+ GDREGISTER_CLASS(VisualShaderNodeScreenNormalWorldSpace);
GDREGISTER_CLASS(VisualShaderNodeIf);
GDREGISTER_CLASS(VisualShaderNodeSwitch);
GDREGISTER_CLASS(VisualShaderNodeFresnel);
@@ -675,6 +691,7 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeProximityFade);
GDREGISTER_CLASS(VisualShaderNodeRandomRange);
GDREGISTER_CLASS(VisualShaderNodeRemap);
+ GDREGISTER_CLASS(VisualShaderNodeRotationByAxis);
GDREGISTER_ABSTRACT_CLASS(VisualShaderNodeVarying);
GDREGISTER_CLASS(VisualShaderNodeVaryingSetter);
GDREGISTER_CLASS(VisualShaderNodeVaryingGetter);
@@ -874,6 +891,14 @@ void register_scene_types() {
GDREGISTER_CLASS(PlaceholderCubemap);
GDREGISTER_CLASS(PlaceholderCubemapArray);
+ // These classes are part of renderer_rd
+ GDREGISTER_CLASS(Texture2DRD);
+ GDREGISTER_ABSTRACT_CLASS(TextureLayeredRD);
+ GDREGISTER_CLASS(Texture2DArrayRD);
+ GDREGISTER_CLASS(TextureCubemapRD);
+ GDREGISTER_CLASS(TextureCubemapArrayRD);
+ GDREGISTER_CLASS(Texture3DRD);
+
GDREGISTER_CLASS(Animation);
GDREGISTER_CLASS(AnimationLibrary);
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 7078d60de5..860e48a361 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -427,6 +427,11 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
} else {
return false;
}
+#ifndef DISABLE_DEPRECATED
+ } else if (prop_name == "loop" && p_value.operator bool()) { // Compatibility with Godot 3.x.
+ loop_mode = Animation::LoopMode::LOOP_LINEAR;
+ return true;
+#endif // DISABLE_DEPRECATED
} else {
return false;
}
@@ -5503,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());
}
@@ -5550,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 1bfcf8d3ac..b6a1737acb 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"
@@ -53,24 +57,24 @@ static const int default_corner_radius = 3;
static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = default_margin, float p_margin_top = default_margin, float p_margin_right = default_margin, float p_margin_bottom = default_margin, int p_corner_radius = default_corner_radius, bool p_draw_center = true, int p_border_width = 0) {
Ref<StyleBoxFlat> style(memnew(StyleBoxFlat));
style->set_bg_color(p_color);
- style->set_content_margin_individual(p_margin_left * scale, p_margin_top * scale, p_margin_right * scale, p_margin_bottom * scale);
+ style->set_content_margin_individual(Math::round(p_margin_left * scale), Math::round(p_margin_top * scale), Math::round(p_margin_right * scale), Math::round(p_margin_bottom * scale));
- style->set_corner_radius_all(p_corner_radius);
+ style->set_corner_radius_all(Math::round(p_corner_radius * scale));
style->set_anti_aliased(true);
// Adjust level of detail based on the corners' effective sizes.
style->set_corner_detail(MIN(Math::ceil(1.5 * p_corner_radius), 6) * scale);
style->set_draw_center(p_draw_center);
- style->set_border_width_all(p_border_width);
+ style->set_border_width_all(Math::round(p_border_width * scale));
return style;
}
static Ref<StyleBoxFlat> sb_expand(Ref<StyleBoxFlat> p_sbox, float p_left, float p_top, float p_right, float p_bottom) {
- p_sbox->set_expand_margin(SIDE_LEFT, p_left * scale);
- p_sbox->set_expand_margin(SIDE_TOP, p_top * scale);
- p_sbox->set_expand_margin(SIDE_RIGHT, p_right * scale);
- p_sbox->set_expand_margin(SIDE_BOTTOM, p_bottom * scale);
+ p_sbox->set_expand_margin(SIDE_LEFT, Math::round(p_left * scale));
+ p_sbox->set_expand_margin(SIDE_TOP, Math::round(p_top * scale));
+ p_sbox->set_expand_margin(SIDE_RIGHT, Math::round(p_right * scale));
+ p_sbox->set_expand_margin(SIDE_BOTTOM, Math::round(p_bottom * scale));
return p_sbox;
}
@@ -83,13 +87,13 @@ static Ref<ImageTexture> generate_icon(int p_index) {
// Generating upsampled icons is slower, and the benefit is hardly visible
// with integer scales.
const bool upsample = !Math::is_equal_approx(Math::round(scale), scale);
- ImageLoaderSVG img_loader;
- Error err = img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>());
+
+ Error err = ImageLoaderSVG::create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>());
ERR_FAIL_COND_V_MSG(err != OK, Ref<ImageTexture>(), "Failed generating icon, unsupported or invalid SVG data in default theme.");
#else
// If the SVG module is disabled, we can't really display the UI well, but at least we won't crash.
// 16 pixels is used as it's the most common base size for Godot icons.
- img = Image::create_empty(16 * scale, 16 * scale, false, Image::FORMAT_RGBA8);
+ img = Image::create_empty(Math::round(16 * scale), Math::round(16 * scale), false, Image::FORMAT_RGBA8);
#endif
return ImageTexture::create_from_image(img);
@@ -97,7 +101,7 @@ static Ref<ImageTexture> generate_icon(int p_index) {
static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) {
Ref<StyleBox> style(memnew(StyleBoxEmpty));
- style->set_content_margin_individual(p_margin_left * scale, p_margin_top * scale, p_margin_right * scale, p_margin_bottom * scale);
+ style->set_content_margin_individual(Math::round(p_margin_left * scale), Math::round(p_margin_top * scale), Math::round(p_margin_right * scale), Math::round(p_margin_bottom * scale));
return style;
}
@@ -106,7 +110,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Default theme properties.
theme->set_default_font(default_font);
- theme->set_default_font_size(default_font_size * scale);
+ theme->set_default_font_size(Math::round(default_font_size * scale));
theme->set_default_base_scale(scale);
// Font colors
@@ -152,7 +156,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
const Ref<StyleBoxFlat> button_disabled = make_flat_stylebox(style_disabled_color);
Ref<StyleBoxFlat> focus = make_flat_stylebox(style_focus_color, default_margin, default_margin, default_margin, default_margin, default_corner_radius, false, 2);
// Make the focus outline appear to be flush with the buttons it's focusing.
- focus->set_expand_margin_all(2 * scale);
+ focus->set_expand_margin_all(Math::round(2 * scale));
theme->set_stylebox("normal", "Button", button_normal);
theme->set_stylebox("hover", "Button", button_hover);
@@ -179,7 +183,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("icon_focus_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 0.4));
- theme->set_constant("h_separation", "Button", 2 * scale);
+ theme->set_constant("h_separation", "Button", Math::round(2 * scale));
theme->set_constant("icon_max_width", "Button", 0);
// MenuBar
@@ -201,7 +205,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "MenuBar", control_font_disabled_color);
theme->set_color("font_outline_color", "MenuBar", Color(1, 1, 1));
- theme->set_constant("h_separation", "MenuBar", 4 * scale);
+ theme->set_constant("h_separation", "MenuBar", Math::round(4 * scale));
// LinkButton
@@ -217,7 +221,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_outline_color", "LinkButton", Color(1, 1, 1));
theme->set_constant("outline_size", "LinkButton", 0);
- theme->set_constant("underline_spacing", "LinkButton", 2 * scale);
+ theme->set_constant("underline_spacing", "LinkButton", Math::round(2 * scale));
// OptionButton
theme->set_stylebox("focus", "OptionButton", focus);
@@ -255,8 +259,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color);
theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1));
- theme->set_constant("h_separation", "OptionButton", 2 * scale);
- theme->set_constant("arrow_margin", "OptionButton", 4 * scale);
+ theme->set_constant("h_separation", "OptionButton", Math::round(2 * scale));
+ theme->set_constant("arrow_margin", "OptionButton", Math::round(4 * scale));
theme->set_constant("outline_size", "OptionButton", 0);
theme->set_constant("modulate_arrow", "OptionButton", false);
@@ -278,15 +282,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "MenuButton", Color(1, 1, 1, 0.3));
theme->set_color("font_outline_color", "MenuButton", Color(1, 1, 1));
- theme->set_constant("h_separation", "MenuButton", 3 * scale);
+ theme->set_constant("h_separation", "MenuButton", Math::round(3 * scale));
theme->set_constant("outline_size", "MenuButton", 0);
// CheckBox
Ref<StyleBox> cbx_empty = memnew(StyleBoxEmpty);
- cbx_empty->set_content_margin_all(4 * scale);
+ cbx_empty->set_content_margin_all(Math::round(4 * scale));
Ref<StyleBox> cbx_focus = focus;
- cbx_focus->set_content_margin_all(4 * scale);
+ cbx_focus->set_content_margin_all(Math::round(4 * scale));
theme->set_stylebox("normal", "CheckBox", cbx_empty);
theme->set_stylebox("pressed", "CheckBox", cbx_empty);
@@ -315,14 +319,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "CheckBox", control_font_disabled_color);
theme->set_color("font_outline_color", "CheckBox", Color(1, 1, 1));
- theme->set_constant("h_separation", "CheckBox", 4 * scale);
+ theme->set_constant("h_separation", "CheckBox", Math::round(4 * scale));
theme->set_constant("check_v_offset", "CheckBox", 0);
theme->set_constant("outline_size", "CheckBox", 0);
// CheckButton
Ref<StyleBox> cb_empty = memnew(StyleBoxEmpty);
- cb_empty->set_content_margin_individual(6 * scale, 4 * scale, 6 * scale, 4 * scale);
+ cb_empty->set_content_margin_individual(Math::round(6 * scale), Math::round(4 * scale), Math::round(6 * scale), Math::round(4 * scale));
theme->set_stylebox("normal", "CheckButton", cb_empty);
theme->set_stylebox("pressed", "CheckButton", cb_empty);
@@ -352,7 +356,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "CheckButton", control_font_disabled_color);
theme->set_color("font_outline_color", "CheckButton", Color(1, 1, 1));
- theme->set_constant("h_separation", "CheckButton", 4 * scale);
+ theme->set_constant("h_separation", "CheckButton", Math::round(4 * scale));
theme->set_constant("check_v_offset", "CheckButton", 0);
theme->set_constant("outline_size", "CheckButton", 0);
@@ -366,11 +370,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_shadow_color", "Label", Color(0, 0, 0, 0));
theme->set_color("font_outline_color", "Label", Color(1, 1, 1));
- theme->set_constant("shadow_offset_x", "Label", 1 * scale);
- theme->set_constant("shadow_offset_y", "Label", 1 * scale);
+ theme->set_constant("shadow_offset_x", "Label", Math::round(1 * scale));
+ theme->set_constant("shadow_offset_y", "Label", Math::round(1 * scale));
theme->set_constant("outline_size", "Label", 0);
- theme->set_constant("shadow_outline_size", "Label", 1 * scale);
- theme->set_constant("line_spacing", "Label", 3 * scale);
+ theme->set_constant("shadow_outline_size", "Label", Math::round(1 * scale));
+ theme->set_constant("line_spacing", "Label", Math::round(3 * scale));
theme->set_type_variation("HeaderSmall", "Label");
theme->set_font_size("font_size", "HeaderSmall", default_font_size + 4);
@@ -456,7 +460,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("search_result_color", "TextEdit", Color(0.3, 0.3, 0.3));
theme->set_color("search_result_border_color", "TextEdit", Color(0.3, 0.3, 0.3, 0.4));
- theme->set_constant("line_spacing", "TextEdit", 4 * scale);
+ theme->set_constant("line_spacing", "TextEdit", Math::round(4 * scale));
theme->set_constant("outline_size", "TextEdit", 0);
theme->set_constant("caret_width", "TextEdit", 1);
@@ -509,7 +513,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("completion_lines", "CodeEdit", 7);
theme->set_constant("completion_max_width", "CodeEdit", 50);
theme->set_constant("completion_scroll_width", "CodeEdit", 6);
- theme->set_constant("line_spacing", "CodeEdit", 4 * scale);
+ theme->set_constant("line_spacing", "CodeEdit", Math::round(4 * scale));
theme->set_constant("outline_size", "CodeEdit", 0);
Ref<Texture2D> empty_icon = memnew(ImageTexture);
@@ -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);
@@ -602,7 +607,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("title_outline_modulate", "Window", Color(1, 1, 1));
theme->set_constant("title_outline_size", "Window", 0);
theme->set_constant("title_height", "Window", 36 * scale);
- theme->set_constant("resize_margin", "Window", 4 * scale);
+ theme->set_constant("resize_margin", "Window", Math::round(4 * scale));
theme->set_icon("close", "Window", icons["close"]);
theme->set_icon("close_pressed", "Window", icons["close_hl"]);
@@ -612,8 +617,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// Dialogs
// AcceptDialog is currently the base dialog, so this defines styles for all extending nodes.
- theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, 8 * scale, 8 * scale, 8 * scale, 8 * scale));
- theme->set_constant("buttons_separation", "AcceptDialog", 10 * scale);
+ theme->set_stylebox("panel", "AcceptDialog", make_flat_stylebox(style_popup_color, Math::round(8 * scale), Math::round(8 * scale), Math::round(8 * scale), Math::round(8 * scale)));
+ theme->set_constant("buttons_separation", "AcceptDialog", Math::round(10 * scale));
// File Dialog
@@ -684,13 +689,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_outline_color", "PopupMenu", Color(1, 1, 1));
theme->set_color("font_separator_outline_color", "PopupMenu", Color(1, 1, 1));
- theme->set_constant("indent", "PopupMenu", 10 * scale);
- theme->set_constant("h_separation", "PopupMenu", 4 * scale);
- theme->set_constant("v_separation", "PopupMenu", 4 * scale);
+ theme->set_constant("indent", "PopupMenu", Math::round(10 * scale));
+ theme->set_constant("h_separation", "PopupMenu", Math::round(4 * scale));
+ theme->set_constant("v_separation", "PopupMenu", Math::round(4 * scale));
theme->set_constant("outline_size", "PopupMenu", 0);
theme->set_constant("separator_outline_size", "PopupMenu", 0);
- theme->set_constant("item_start_padding", "PopupMenu", 2 * scale);
- theme->set_constant("item_end_padding", "PopupMenu", 2 * scale);
+ theme->set_constant("item_start_padding", "PopupMenu", Math::round(2 * scale));
+ theme->set_constant("item_end_padding", "PopupMenu", Math::round(2 * scale));
theme->set_constant("icon_max_width", "PopupMenu", 0);
// GraphNode
@@ -711,8 +716,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("frame", "GraphNode", graphnode_normal);
theme->set_stylebox("selected_frame", "GraphNode", graphnode_selected);
- theme->set_stylebox("comment", "GraphNode", graphnode_comment_normal);
- theme->set_stylebox("comment_focus", "GraphNode", graphnode_comment_selected);
theme->set_stylebox("breakpoint", "GraphNode", graphnode_breakpoint);
theme->set_stylebox("position", "GraphNode", graphnode_position);
theme->set_stylebox("slot", "GraphNode", graphnode_slot);
@@ -724,11 +727,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("title_color", "GraphNode", control_font_color);
theme->set_color("close_color", "GraphNode", control_font_color);
theme->set_color("resizer_color", "GraphNode", control_font_color);
- theme->set_constant("separation", "GraphNode", 2 * scale);
- theme->set_constant("title_offset", "GraphNode", 26 * scale);
+ theme->set_constant("separation", "GraphNode", Math::round(2 * scale));
+ theme->set_constant("title_offset", "GraphNode", Math::round(26 * scale));
theme->set_constant("title_h_offset", "GraphNode", 0);
- theme->set_constant("close_offset", "GraphNode", 22 * scale);
- theme->set_constant("close_h_offset", "GraphNode", 12 * scale);
+ theme->set_constant("close_offset", "GraphNode", Math::round(22 * scale));
+ theme->set_constant("close_h_offset", "GraphNode", Math::round(12 * scale));
theme->set_constant("port_offset", "GraphNode", 0);
// Tree
@@ -771,10 +774,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("children_hl_line_color", "Tree", Color(0.27, 0.27, 0.27));
theme->set_color("custom_button_font_highlight", "Tree", control_font_hover_color);
- theme->set_constant("h_separation", "Tree", 4 * scale);
- theme->set_constant("v_separation", "Tree", 4 * scale);
- theme->set_constant("item_margin", "Tree", 16 * scale);
- theme->set_constant("button_margin", "Tree", 4 * scale);
+ theme->set_constant("h_separation", "Tree", Math::round(4 * scale));
+ theme->set_constant("v_separation", "Tree", Math::round(4 * scale));
+ theme->set_constant("item_margin", "Tree", Math::round(16 * scale));
+ theme->set_constant("inner_item_margin_bottom", "Tree", 0);
+ theme->set_constant("inner_item_margin_left", "Tree", 0);
+ theme->set_constant("inner_item_margin_right", "Tree", 0);
+ theme->set_constant("inner_item_margin_top", "Tree", 0);
+ theme->set_constant("button_margin", "Tree", Math::round(4 * scale));
theme->set_constant("draw_relationship_lines", "Tree", 0);
theme->set_constant("relationship_line_width", "Tree", 1);
theme->set_constant("parent_hl_line_width", "Tree", 1);
@@ -789,8 +796,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("scrollbar_margin_top", "Tree", -1);
theme->set_constant("scrollbar_margin_right", "Tree", -1);
theme->set_constant("scrollbar_margin_bottom", "Tree", -1);
- theme->set_constant("scrollbar_h_separation", "Tree", 4 * scale);
- theme->set_constant("scrollbar_v_separation", "Tree", 4 * scale);
+ theme->set_constant("scrollbar_h_separation", "Tree", Math::round(4 * scale));
+ theme->set_constant("scrollbar_v_separation", "Tree", Math::round(4 * scale));
// ItemList
@@ -799,7 +806,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("h_separation", "ItemList", 4);
theme->set_constant("v_separation", "ItemList", 2);
theme->set_constant("icon_margin", "ItemList", 4);
- theme->set_constant("line_separation", "ItemList", 2 * scale);
+ theme->set_constant("line_separation", "ItemList", Math::round(2 * scale));
theme->set_font("font", "ItemList", Ref<Font>());
theme->set_font_size("font_size", "ItemList", -1);
@@ -857,8 +864,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_outline_color", "TabContainer", Color(1, 1, 1));
theme->set_color("drop_mark_color", "TabContainer", Color(1, 1, 1));
- theme->set_constant("side_margin", "TabContainer", 8 * scale);
- theme->set_constant("icon_separation", "TabContainer", 4 * scale);
+ theme->set_constant("side_margin", "TabContainer", Math::round(8 * scale));
+ theme->set_constant("icon_separation", "TabContainer", Math::round(4 * scale));
theme->set_constant("icon_max_width", "TabContainer", 0);
theme->set_constant("outline_size", "TabContainer", 0);
@@ -888,7 +895,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_outline_color", "TabBar", Color(1, 1, 1));
theme->set_color("drop_mark_color", "TabBar", Color(1, 1, 1));
- theme->set_constant("h_separation", "TabBar", 4 * scale);
+ theme->set_constant("h_separation", "TabBar", Math::round(4 * scale));
theme->set_constant("icon_max_width", "TabBar", 0);
theme->set_constant("outline_size", "TabBar", 0);
@@ -901,16 +908,16 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_font("normal", "Fonts", Ref<Font>());
theme->set_font("large", "Fonts", Ref<Font>());
- theme->set_constant("separation", "HSeparator", 4 * scale);
- theme->set_constant("separation", "VSeparator", 4 * scale);
+ theme->set_constant("separation", "HSeparator", Math::round(4 * scale));
+ theme->set_constant("separation", "VSeparator", Math::round(4 * scale));
// ColorPicker
- theme->set_constant("margin", "ColorPicker", 4 * scale);
- theme->set_constant("sv_width", "ColorPicker", 256 * scale);
- theme->set_constant("sv_height", "ColorPicker", 256 * scale);
- theme->set_constant("h_width", "ColorPicker", 30 * scale);
- theme->set_constant("label_width", "ColorPicker", 10 * scale);
+ theme->set_constant("margin", "ColorPicker", Math::round(4 * scale));
+ theme->set_constant("sv_width", "ColorPicker", Math::round(256 * scale));
+ theme->set_constant("sv_height", "ColorPicker", Math::round(256 * scale));
+ theme->set_constant("h_width", "ColorPicker", Math::round(30 * scale));
+ theme->set_constant("label_width", "ColorPicker", Math::round(10 * scale));
theme->set_constant("center_slider_grabbers", "ColorPicker", 1);
theme->set_icon("folded_arrow", "ColorPicker", icons["arrow_right"]);
@@ -998,14 +1005,14 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1));
- theme->set_constant("h_separation", "ColorPickerButton", 2 * scale);
+ theme->set_constant("h_separation", "ColorPickerButton", Math::round(2 * scale));
theme->set_constant("outline_size", "ColorPickerButton", 0);
// ColorPresetButton
Ref<StyleBoxFlat> preset_sb = make_flat_stylebox(Color(1, 1, 1), 2, 2, 2, 2);
- preset_sb->set_corner_radius_all(2);
- preset_sb->set_corner_detail(2);
+ preset_sb->set_corner_radius_all(Math::round(2 * scale));
+ preset_sb->set_corner_detail(Math::round(2 * scale));
preset_sb->set_anti_aliased(false);
theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb);
@@ -1052,13 +1059,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_outline_color", "RichTextLabel", Color(1, 1, 1));
- theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * scale);
- theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale);
- theme->set_constant("shadow_outline_size", "RichTextLabel", 1 * scale);
+ theme->set_constant("shadow_offset_x", "RichTextLabel", Math::round(1 * scale));
+ theme->set_constant("shadow_offset_y", "RichTextLabel", Math::round(1 * scale));
+ theme->set_constant("shadow_outline_size", "RichTextLabel", Math::round(1 * scale));
theme->set_constant("line_separation", "RichTextLabel", 0);
- theme->set_constant("table_h_separation", "RichTextLabel", 3 * scale);
- theme->set_constant("table_v_separation", "RichTextLabel", 3 * scale);
+ theme->set_constant("table_h_separation", "RichTextLabel", Math::round(3 * scale));
+ theme->set_constant("table_v_separation", "RichTextLabel", Math::round(3 * scale));
theme->set_constant("outline_size", "RichTextLabel", 0);
@@ -1066,8 +1073,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("table_even_row_bg", "RichTextLabel", Color(0, 0, 0, 0));
theme->set_color("table_border", "RichTextLabel", Color(0, 0, 0, 0));
- theme->set_constant("text_highlight_h_padding", "RichTextLabel", 3 * scale);
- theme->set_constant("text_highlight_v_padding", "RichTextLabel", 3 * scale);
+ theme->set_constant("text_highlight_h_padding", "RichTextLabel", Math::round(3 * scale));
+ theme->set_constant("text_highlight_v_padding", "RichTextLabel", Math::round(3 * scale));
// Containers
@@ -1076,40 +1083,43 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("grabber", "VSplitContainer", icons["vsplitter"]);
theme->set_icon("grabber", "HSplitContainer", icons["hsplitter"]);
- theme->set_constant("separation", "BoxContainer", 4 * scale);
- theme->set_constant("separation", "HBoxContainer", 4 * scale);
- theme->set_constant("separation", "VBoxContainer", 4 * scale);
+ theme->set_constant("separation", "BoxContainer", Math::round(4 * scale));
+ theme->set_constant("separation", "HBoxContainer", Math::round(4 * scale));
+ theme->set_constant("separation", "VBoxContainer", Math::round(4 * scale));
theme->set_constant("margin_left", "MarginContainer", 0);
theme->set_constant("margin_top", "MarginContainer", 0);
theme->set_constant("margin_right", "MarginContainer", 0);
theme->set_constant("margin_bottom", "MarginContainer", 0);
- theme->set_constant("h_separation", "GridContainer", 4 * scale);
- theme->set_constant("v_separation", "GridContainer", 4 * scale);
- theme->set_constant("separation", "SplitContainer", 12 * scale);
- theme->set_constant("separation", "HSplitContainer", 12 * scale);
- theme->set_constant("separation", "VSplitContainer", 12 * scale);
- theme->set_constant("minimum_grab_thickness", "SplitContainer", 6 * scale);
- theme->set_constant("minimum_grab_thickness", "HSplitContainer", 6 * scale);
- theme->set_constant("minimum_grab_thickness", "VSplitContainer", 6 * scale);
+ theme->set_constant("h_separation", "GridContainer", Math::round(4 * scale));
+ theme->set_constant("v_separation", "GridContainer", Math::round(4 * scale));
+ theme->set_constant("separation", "SplitContainer", Math::round(12 * scale));
+ theme->set_constant("separation", "HSplitContainer", Math::round(12 * scale));
+ theme->set_constant("separation", "VSplitContainer", Math::round(12 * scale));
+ theme->set_constant("minimum_grab_thickness", "SplitContainer", Math::round(6 * scale));
+ theme->set_constant("minimum_grab_thickness", "HSplitContainer", Math::round(6 * scale));
+ theme->set_constant("minimum_grab_thickness", "VSplitContainer", Math::round(6 * scale));
theme->set_constant("autohide", "SplitContainer", 1);
theme->set_constant("autohide", "HSplitContainer", 1);
theme->set_constant("autohide", "VSplitContainer", 1);
- theme->set_constant("h_separation", "FlowContainer", 4 * scale);
- theme->set_constant("v_separation", "FlowContainer", 4 * scale);
- theme->set_constant("h_separation", "HFlowContainer", 4 * scale);
- theme->set_constant("v_separation", "HFlowContainer", 4 * scale);
- theme->set_constant("h_separation", "VFlowContainer", 4 * scale);
- theme->set_constant("v_separation", "VFlowContainer", 4 * scale);
+ theme->set_constant("h_separation", "FlowContainer", Math::round(4 * scale));
+ theme->set_constant("v_separation", "FlowContainer", Math::round(4 * scale));
+ theme->set_constant("h_separation", "HFlowContainer", Math::round(4 * scale));
+ theme->set_constant("v_separation", "HFlowContainer", Math::round(4 * scale));
+ theme->set_constant("h_separation", "VFlowContainer", Math::round(4 * scale));
+ theme->set_constant("v_separation", "VFlowContainer", Math::round(4 * scale));
theme->set_stylebox("panel", "PanelContainer", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
- theme->set_icon("minus", "GraphEdit", icons["zoom_less"]);
- theme->set_icon("reset", "GraphEdit", icons["zoom_reset"]);
- theme->set_icon("more", "GraphEdit", icons["zoom_more"]);
- theme->set_icon("snap", "GraphEdit", icons["grid_snap"]);
- theme->set_icon("minimap", "GraphEdit", icons["grid_minimap"]);
+ theme->set_icon("zoom_out", "GraphEdit", icons["zoom_less"]);
+ theme->set_icon("zoom_in", "GraphEdit", icons["zoom_more"]);
+ theme->set_icon("zoom_reset", "GraphEdit", icons["zoom_reset"]);
+ theme->set_icon("grid_toggle", "GraphEdit", icons["grid_toggle"]);
+ theme->set_icon("minimap_toggle", "GraphEdit", icons["grid_minimap"]);
+ theme->set_icon("snapping_toggle", "GraphEdit", icons["grid_snap"]);
theme->set_icon("layout", "GraphEdit", icons["grid_layout"]);
- theme->set_stylebox("bg", "GraphEdit", make_flat_stylebox(style_normal_color, 4, 4, 4, 5));
+
+ theme->set_stylebox("panel", "GraphEdit", make_flat_stylebox(style_normal_color, 4, 4, 4, 5));
+
theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05));
theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2));
theme->set_color("selection_fill", "GraphEdit", Color(1, 1, 1, 0.3));
@@ -1121,7 +1131,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("port_hotzone_inner_extent", "GraphEdit", 22 * scale);
theme->set_constant("port_hotzone_outer_extent", "GraphEdit", 26 * scale);
- theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0));
+ theme->set_stylebox("panel", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0));
Ref<StyleBoxFlat> style_minimap_camera = make_flat_stylebox(Color(0.65, 0.65, 0.65, 0.2), 0, 0, 0, 0, 0);
style_minimap_camera->set_border_color(Color(0.65, 0.65, 0.65, 0.45));
style_minimap_camera->set_border_width_all(1);
diff --git a/scene/resources/default_theme/grid_toggle.svg b/scene/resources/default_theme/grid_toggle.svg
new file mode 100644
index 0000000000..b0721db518
--- /dev/null
+++ b/scene/resources/default_theme/grid_toggle.svg
@@ -0,0 +1 @@
+<svg viewBox="0 0 16 16" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 0v3H0v2h3v4H0v2h3v3h2V5h9V3h-3V0H9v3H5V0z" fill="#b2b2b2" fill-opacity=".65"/><path d="M11 6.62c-1.747 0-3.957 1.344-4.752 3.936a.683.69 0 00-.004.394C7.012 13.665 9.292 14.9 11 14.9c1.708 0 3.988-1.235 4.756-3.95a.683.69 0 000-.382C15.004 7.955 12.746 6.62 11 6.62zM11 8a2.733 2.76 0 012.733 2.76A2.733 2.76 0 0111 13.52a2.733 2.76 0 01-2.733-2.76A2.733 2.76 0 0111 8zm0 1.38a1.367 1.38 0 00-1.367 1.38A1.367 1.38 0 0011 12.14a1.367 1.38 0 001.367-1.38A1.367 1.38 0 0011 9.38z" fill="#e0e0e0" /></svg>
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..ecf70d96ac
--- /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 | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT), "_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..65da0bc3c8 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));
}
}
@@ -2876,7 +2876,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_proximity_fade_distance", "get_proximity_fade_distance");
ADD_GROUP("MSDF", "msdf_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), "set_msdf_pixel_range", "get_msdf_pixel_range");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_outline_size", PROPERTY_HINT_RANGE, "1,250,1"), "set_msdf_outline_size", "get_msdf_outline_size");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_outline_size", PROPERTY_HINT_RANGE, "0,250,1"), "set_msdf_outline_size", "get_msdf_outline_size");
ADD_GROUP("Distance Fade", "distance_fade_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "distance_fade_mode", PROPERTY_HINT_ENUM, "Disabled,PixelAlpha,PixelDither,ObjectDither"), "set_distance_fade", "get_distance_fade");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_distance_fade_min_distance", "get_distance_fade_min_distance");
@@ -3064,6 +3064,9 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
set_grow(0.0);
+ set_msdf_pixel_range(4.0);
+ set_msdf_outline_size(0.0);
+
set_heightmap_deep_parallax_min_layers(8);
set_heightmap_deep_parallax_max_layers(32);
set_heightmap_deep_parallax_flip_tangent(false); //also sets binormal
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/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index 1d13f07b12..82b5c6257c 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -341,6 +341,11 @@ void NavigationMesh::clear_polygons() {
polygons.clear();
}
+void NavigationMesh::clear() {
+ polygons.clear();
+ vertices.clear();
+}
+
#ifdef DEBUG_ENABLED
Ref<ArrayMesh> NavigationMesh::get_debug_mesh() {
if (debug_mesh.is_valid()) {
@@ -518,6 +523,8 @@ void NavigationMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationMesh::_set_polygons);
ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationMesh::_get_polygons);
+ ClassDB::bind_method(D_METHOD("clear"), &NavigationMesh::clear);
+
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h
index c3b8c13c05..8b9b810038 100644
--- a/scene/resources/navigation_mesh.h
+++ b/scene/resources/navigation_mesh.h
@@ -43,19 +43,6 @@ class NavigationMesh : public Resource {
Vector<Polygon> polygons;
Ref<ArrayMesh> debug_mesh;
- struct _EdgeKey {
- Vector3 from;
- Vector3 to;
-
- static uint32_t hash(const _EdgeKey &p_key) {
- return HashMapHasherDefault::hash(p_key.from) ^ HashMapHasherDefault::hash(p_key.to);
- }
-
- bool operator==(const _EdgeKey &p_with) const {
- return HashMapComparatorDefault<Vector3>::compare(from, p_with.from) && HashMapComparatorDefault<Vector3>::compare(to, p_with.to);
- }
- };
-
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const;
@@ -99,7 +86,7 @@ protected:
float agent_max_slope = 45.0f;
float region_min_size = 2.0f;
float region_merge_size = 20.0f;
- float edge_max_length = 12.0f;
+ float edge_max_length = 0.0f;
float edge_max_error = 1.3f;
float vertices_per_polygon = 6.0f;
float detail_sample_distance = 6.0f;
@@ -202,6 +189,8 @@ public:
Vector<int> get_polygon(int p_idx);
void clear_polygons();
+ void clear();
+
#ifdef DEBUG_ENABLED
Ref<ArrayMesh> get_debug_mesh();
#endif // DEBUG_ENABLED
diff --git a/scene/resources/navigation_polygon.cpp b/scene/resources/navigation_polygon.cpp
index 0b60d46d40..e521bfb2e0 100644
--- a/scene/resources/navigation_polygon.cpp
+++ b/scene/resources/navigation_polygon.cpp
@@ -162,6 +162,15 @@ void NavigationPolygon::clear_polygons() {
}
}
+void NavigationPolygon::clear() {
+ polygons.clear();
+ vertices.clear();
+ {
+ MutexLock lock(navigation_mesh_generation);
+ navigation_mesh.unref();
+ }
+}
+
Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() {
MutexLock lock(navigation_mesh_generation);
@@ -360,6 +369,8 @@ void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &NavigationPolygon::set_cell_size);
ClassDB::bind_method(D_METHOD("get_cell_size"), &NavigationPolygon::get_cell_size);
+ ClassDB::bind_method(D_METHOD("clear"), &NavigationPolygon::clear);
+
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines");
diff --git a/scene/resources/navigation_polygon.h b/scene/resources/navigation_polygon.h
index 3ab666eb16..7926709a9e 100644
--- a/scene/resources/navigation_polygon.h
+++ b/scene/resources/navigation_polygon.h
@@ -92,6 +92,8 @@ public:
void set_cell_size(real_t p_cell_size);
real_t get_cell_size() const;
+ void clear();
+
NavigationPolygon() {}
~NavigationPolygon() {}
};
diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h
index 35799dc1ee..2be3ac08af 100644
--- a/scene/resources/packed_scene.h
+++ b/scene/resources/packed_scene.h
@@ -255,6 +255,7 @@ public:
virtual void set_path(const String &p_path, bool p_take_over = false) override;
#ifdef TOOLS_ENABLED
virtual void set_last_modified_time(uint64_t p_time) override {
+ Resource::set_last_modified_time(p_time);
state->set_last_modified_time(p_time);
}
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..c395195c47
--- /dev/null
+++ b/scene/resources/placeholder_textures.cpp
@@ -0,0 +1,197 @@
+/**************************************************************************/
+/* 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 {
+ if (rid.is_null()) {
+ rid = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+ 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() {
+}
+
+PlaceholderTexture2D::~PlaceholderTexture2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ if (rid.is_valid()) {
+ 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>>();
+}
+
+RID PlaceholderTexture3D::get_rid() const {
+ if (rid.is_null()) {
+ rid = RenderingServer::get_singleton()->texture_3d_placeholder_create();
+ }
+ return rid;
+}
+
+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() {
+}
+PlaceholderTexture3D::~PlaceholderTexture3D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ if (rid.is_valid()) {
+ 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>();
+}
+
+RID PlaceholderTextureLayered::get_rid() const {
+ if (rid.is_null()) {
+ rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type));
+ }
+ return rid;
+}
+
+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;
+}
+PlaceholderTextureLayered::~PlaceholderTextureLayered() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ if (rid.is_valid()) {
+ 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..300d641cc3
--- /dev/null
+++ b/scene/resources/placeholder_textures.h
@@ -0,0 +1,132 @@
+/**************************************************************************/
+/* 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)
+
+ mutable 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)
+
+ mutable 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;
+ virtual RID get_rid() const override;
+
+ PlaceholderTexture3D();
+ ~PlaceholderTexture3D();
+};
+
+class PlaceholderTextureLayered : public TextureLayered {
+ GDCLASS(PlaceholderTextureLayered, TextureLayered)
+
+ mutable 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;
+ virtual RID get_rid() 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..a61799c7d6
--- /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 7719cc28d2..a2c04698e1 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1828,6 +1828,10 @@ String ResourceFormatSaverTextInstance::_write_resources(void *ud, const Ref<Res
}
String ResourceFormatSaverTextInstance::_write_resource(const Ref<Resource> &res) {
+ if (res->get_meta(SNAME("_skip_save_"), false)) {
+ return "null";
+ }
+
if (external_resources.has(res)) {
return "ExtResource(\"" + external_resources[res] + "\")";
} else {
@@ -1852,7 +1856,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
case Variant::OBJECT: {
Ref<Resource> res = p_variant;
- if (res.is_null() || external_resources.has(res)) {
+ if (res.is_null() || external_resources.has(res) || res->get_meta(SNAME("_skip_save_"), false)) {
return;
}
@@ -1873,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);
@@ -1887,14 +1893,17 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
Variant v = res->get(I->get().name);
if (pi.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
+ NonPersistentKey npk;
+ npk.base = res;
+ npk.property = pi.name;
+ non_persistent_map[npk] = v;
+
Ref<Resource> sres = v;
if (sres.is_valid()) {
- NonPersistentKey npk;
- npk.base = res;
- npk.property = pi.name;
- non_persistent_map[npk] = sres;
resource_set.insert(sres);
saved_resources.push_back(sres);
+ } else {
+ _find_resources(v);
}
} else {
_find_resources(v);
@@ -1904,8 +1913,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/resource_format_text.h b/scene/resources/resource_format_text.h
index c35a594f72..1734ccc98b 100644
--- a/scene/resources/resource_format_text.h
+++ b/scene/resources/resource_format_text.h
@@ -171,7 +171,7 @@ class ResourceFormatSaverTextInstance {
bool operator<(const NonPersistentKey &p_key) const { return base == p_key.base ? property < p_key.property : base < p_key.base; }
};
- RBMap<NonPersistentKey, Ref<Resource>> non_persistent_map;
+ RBMap<NonPersistentKey, Variant> non_persistent_map;
HashSet<Ref<Resource>> resource_set;
List<Ref<Resource>> saved_resources;
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/texture_rd.cpp b/scene/resources/texture_rd.cpp
new file mode 100644
index 0000000000..6f25af6863
--- /dev/null
+++ b/scene/resources/texture_rd.cpp
@@ -0,0 +1,346 @@
+/**************************************************************************/
+/* texture_rd.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 "texture_rd.h"
+
+////////////////////////////////////////////////////////////////////////////
+// Texture2DRD
+
+void Texture2DRD::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture_rd_rid", "texture_rd_rid"), &Texture2DRD::set_texture_rd_rid);
+ ClassDB::bind_method(D_METHOD("get_texture_rd_rid"), &Texture2DRD::get_texture_rd_rid);
+
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "texture_rd_rid"), "set_texture_rd_rid", "get_texture_rd_rid");
+}
+
+int Texture2DRD::get_width() const {
+ return size.width;
+}
+
+int Texture2DRD::get_height() const {
+ return size.height;
+}
+
+RID Texture2DRD::get_rid() const {
+ if (texture_rid.is_null()) {
+ // We are in trouble, create something temporary.
+ texture_rid = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+
+ return texture_rid;
+}
+
+bool Texture2DRD::has_alpha() const {
+ return false;
+}
+
+Ref<Image> Texture2DRD::get_image() const {
+ ERR_FAIL_NULL_V(RS::get_singleton(), Ref<Image>());
+ if (texture_rid.is_valid()) {
+ return RS::get_singleton()->texture_2d_get(texture_rid);
+ } else {
+ return Ref<Image>();
+ }
+}
+
+void Texture2DRD::set_texture_rd_rid(RID p_texture_rd_rid) {
+ ERR_FAIL_NULL(RS::get_singleton());
+
+ if (p_texture_rd_rid.is_valid()) {
+ ERR_FAIL_NULL(RD::get_singleton());
+ ERR_FAIL_COND(!RD::get_singleton()->texture_is_valid(p_texture_rd_rid));
+
+ RD::TextureFormat tf = RD::get_singleton()->texture_get_format(p_texture_rd_rid);
+ ERR_FAIL_COND(tf.texture_type != RD::TEXTURE_TYPE_2D);
+ ERR_FAIL_COND(tf.depth > 1);
+ ERR_FAIL_COND(tf.array_layers > 1);
+
+ size.width = tf.width;
+ size.height = tf.height;
+
+ texture_rd_rid = p_texture_rd_rid;
+
+ if (texture_rid.is_valid()) {
+ RS::get_singleton()->texture_replace(texture_rid, RS::get_singleton()->texture_rd_create(p_texture_rd_rid));
+ } else {
+ texture_rid = RS::get_singleton()->texture_rd_create(p_texture_rd_rid);
+ }
+
+ notify_property_list_changed();
+ emit_changed();
+ } else if (texture_rid.is_valid()) {
+ RS::get_singleton()->free(texture_rid);
+ texture_rid = RID();
+ size = Size2i();
+
+ notify_property_list_changed();
+ emit_changed();
+ }
+}
+
+RID Texture2DRD::get_texture_rd_rid() const {
+ return texture_rd_rid;
+}
+
+Texture2DRD::Texture2DRD() {
+ size = Size2i();
+}
+
+Texture2DRD::~Texture2DRD() {
+ if (texture_rid.is_valid()) {
+ ERR_FAIL_NULL(RS::get_singleton());
+ RS::get_singleton()->free(texture_rid);
+ texture_rid = RID();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+// TextureLayeredRD
+
+void TextureLayeredRD::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture_rd_rid", "texture_rd_rid"), &TextureLayeredRD::set_texture_rd_rid);
+ ClassDB::bind_method(D_METHOD("get_texture_rd_rid"), &TextureLayeredRD::get_texture_rd_rid);
+
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "texture_rd_rid"), "set_texture_rd_rid", "get_texture_rd_rid");
+}
+
+TextureLayered::LayeredType TextureLayeredRD::get_layered_type() const {
+ return layer_type;
+}
+
+Image::Format TextureLayeredRD::get_format() const {
+ return image_format;
+}
+
+int TextureLayeredRD::get_width() const {
+ return size.width;
+}
+
+int TextureLayeredRD::get_height() const {
+ return size.height;
+}
+
+int TextureLayeredRD::get_layers() const {
+ return (int)layers;
+}
+
+bool TextureLayeredRD::has_mipmaps() const {
+ return mipmaps > 1;
+}
+
+RID TextureLayeredRD::get_rid() const {
+ if (texture_rid.is_null()) {
+ // We are in trouble, create something temporary.
+ texture_rid = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+
+ return texture_rid;
+}
+
+Ref<Image> TextureLayeredRD::get_layer_data(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers, Ref<Image>());
+ return RS::get_singleton()->texture_2d_layer_get(texture_rid, p_layer);
+}
+
+void TextureLayeredRD::set_texture_rd_rid(RID p_texture_rd_rid) {
+ ERR_FAIL_NULL(RS::get_singleton());
+
+ if (p_texture_rd_rid.is_valid()) {
+ ERR_FAIL_NULL(RD::get_singleton());
+ ERR_FAIL_COND(!RD::get_singleton()->texture_is_valid(p_texture_rd_rid));
+
+ RS::TextureLayeredType rs_layer_type;
+ RD::TextureFormat tf = RD::get_singleton()->texture_get_format(p_texture_rd_rid);
+ ERR_FAIL_COND(tf.texture_type != RD::TEXTURE_TYPE_2D_ARRAY);
+ ERR_FAIL_COND(tf.depth > 1);
+ switch (layer_type) {
+ case LAYERED_TYPE_2D_ARRAY: {
+ ERR_FAIL_COND(tf.array_layers <= 1);
+ rs_layer_type = RS::TEXTURE_LAYERED_2D_ARRAY;
+ } break;
+ case LAYERED_TYPE_CUBEMAP: {
+ ERR_FAIL_COND(tf.array_layers != 6);
+ rs_layer_type = RS::TEXTURE_LAYERED_CUBEMAP;
+ } break;
+ case LAYERED_TYPE_CUBEMAP_ARRAY: {
+ ERR_FAIL_COND((tf.array_layers == 0) || ((tf.array_layers % 6) != 0));
+ rs_layer_type = RS::TEXTURE_LAYERED_CUBEMAP_ARRAY;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Unknown layer type selected");
+ } break;
+ }
+
+ size.width = tf.width;
+ size.height = tf.height;
+ layers = tf.array_layers;
+ mipmaps = tf.mipmaps;
+
+ texture_rd_rid = p_texture_rd_rid;
+
+ if (texture_rid.is_valid()) {
+ RS::get_singleton()->texture_replace(texture_rid, RS::get_singleton()->texture_rd_create(p_texture_rd_rid, rs_layer_type));
+ } else {
+ texture_rid = RS::get_singleton()->texture_rd_create(p_texture_rd_rid, rs_layer_type);
+ }
+
+ image_format = RS::get_singleton()->texture_get_format(texture_rid);
+
+ notify_property_list_changed();
+ emit_changed();
+ } else if (texture_rid.is_valid()) {
+ RS::get_singleton()->free(texture_rid);
+ texture_rid = RID();
+ image_format = Image::FORMAT_MAX;
+ size = Size2i();
+ layers = 0;
+ mipmaps = 0;
+
+ notify_property_list_changed();
+ emit_changed();
+ }
+}
+
+RID TextureLayeredRD::get_texture_rd_rid() const {
+ return texture_rd_rid;
+}
+
+TextureLayeredRD::TextureLayeredRD(LayeredType p_layer_type) {
+ layer_type = p_layer_type;
+ size = Size2i();
+ image_format = Image::FORMAT_MAX;
+ layers = 0;
+ mipmaps = 0;
+}
+
+TextureLayeredRD::~TextureLayeredRD() {
+ if (texture_rid.is_valid()) {
+ ERR_FAIL_NULL(RS::get_singleton());
+ RS::get_singleton()->free(texture_rid);
+ texture_rid = RID();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Texture3DRD
+
+void Texture3DRD::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_texture_rd_rid", "texture_rd_rid"), &Texture3DRD::set_texture_rd_rid);
+ ClassDB::bind_method(D_METHOD("get_texture_rd_rid"), &Texture3DRD::get_texture_rd_rid);
+
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "texture_rd_rid"), "set_texture_rd_rid", "get_texture_rd_rid");
+}
+
+Image::Format Texture3DRD::get_format() const {
+ return image_format;
+}
+
+int Texture3DRD::get_width() const {
+ return size.x;
+}
+
+int Texture3DRD::get_height() const {
+ return size.y;
+}
+
+int Texture3DRD::get_depth() const {
+ return size.z;
+}
+
+bool Texture3DRD::has_mipmaps() const {
+ return mipmaps > 1;
+}
+
+RID Texture3DRD::get_rid() const {
+ if (texture_rid.is_null()) {
+ // We are in trouble, create something temporary.
+ texture_rid = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+
+ return texture_rid;
+}
+
+void Texture3DRD::set_texture_rd_rid(RID p_texture_rd_rid) {
+ ERR_FAIL_NULL(RS::get_singleton());
+
+ if (p_texture_rd_rid.is_valid()) {
+ ERR_FAIL_NULL(RD::get_singleton());
+ ERR_FAIL_COND(!RD::get_singleton()->texture_is_valid(p_texture_rd_rid));
+
+ RD::TextureFormat tf = RD::get_singleton()->texture_get_format(p_texture_rd_rid);
+ ERR_FAIL_COND(tf.texture_type != RD::TEXTURE_TYPE_3D);
+ ERR_FAIL_COND(tf.array_layers > 1);
+
+ size.x = tf.width;
+ size.y = tf.height;
+ size.z = tf.depth;
+ mipmaps = tf.mipmaps;
+
+ texture_rd_rid = p_texture_rd_rid;
+
+ if (texture_rid.is_valid()) {
+ RS::get_singleton()->texture_replace(texture_rid, RS::get_singleton()->texture_rd_create(p_texture_rd_rid));
+ } else {
+ texture_rid = RS::get_singleton()->texture_rd_create(p_texture_rd_rid);
+ }
+
+ image_format = RS::get_singleton()->texture_get_format(texture_rid);
+
+ notify_property_list_changed();
+ emit_changed();
+ } else if (texture_rid.is_valid()) {
+ RS::get_singleton()->free(texture_rid);
+ texture_rid = RID();
+ image_format = Image::FORMAT_MAX;
+ size = Vector3i();
+ mipmaps = 0;
+
+ notify_property_list_changed();
+ emit_changed();
+ }
+}
+
+RID Texture3DRD::get_texture_rd_rid() const {
+ return texture_rd_rid;
+}
+
+Texture3DRD::Texture3DRD() {
+ image_format = Image::FORMAT_MAX;
+ size = Vector3i();
+ mipmaps = 0;
+}
+
+Texture3DRD::~Texture3DRD() {
+ if (texture_rid.is_valid()) {
+ ERR_FAIL_NULL(RS::get_singleton());
+ RS::get_singleton()->free(texture_rid);
+ texture_rid = RID();
+ }
+}
diff --git a/scene/resources/texture_rd.h b/scene/resources/texture_rd.h
new file mode 100644
index 0000000000..f88d6c5155
--- /dev/null
+++ b/scene/resources/texture_rd.h
@@ -0,0 +1,153 @@
+/**************************************************************************/
+/* texture_rd.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 TEXTURE_RD_H
+#define TEXTURE_RD_H
+
+// Note, these classes are part of the Rendering Device based renderer.
+// They are included here to ensure the correct order of registration
+// is performed.
+// Once the renderer has been moved into a module, these classes should
+// be moved as well.
+
+#include "scene/resources/texture.h"
+
+class Texture2DRD : public Texture2D {
+ GDCLASS(Texture2DRD, Texture2D)
+
+ mutable RID texture_rid;
+ RID texture_rd_rid;
+ 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;
+
+ virtual Ref<Image> get_image() const override;
+
+ void set_texture_rd_rid(RID p_texture_rd_rid);
+ RID get_texture_rd_rid() const;
+
+ Texture2DRD();
+ ~Texture2DRD();
+};
+
+class TextureLayeredRD : public TextureLayered {
+ GDCLASS(TextureLayeredRD, TextureLayered)
+
+ LayeredType layer_type;
+
+ mutable RID texture_rid;
+ RID texture_rd_rid;
+
+ Image::Format image_format;
+ Size2i size;
+ uint32_t layers = 0;
+ uint32_t mipmaps = 0;
+
+protected:
+ static void _bind_methods();
+
+public:
+ 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 RID get_rid() const override;
+
+ virtual Ref<Image> get_layer_data(int p_layer) const override;
+
+ void set_texture_rd_rid(RID p_texture_rd_rid);
+ RID get_texture_rd_rid() const;
+
+ TextureLayeredRD(LayeredType p_layer_type);
+ ~TextureLayeredRD();
+};
+
+class Texture2DArrayRD : public TextureLayeredRD {
+ GDCLASS(Texture2DArrayRD, TextureLayeredRD)
+
+public:
+ Texture2DArrayRD() :
+ TextureLayeredRD(LAYERED_TYPE_2D_ARRAY) {}
+};
+
+class TextureCubemapRD : public TextureLayeredRD {
+ GDCLASS(TextureCubemapRD, TextureLayeredRD)
+
+public:
+ TextureCubemapRD() :
+ TextureLayeredRD(LAYERED_TYPE_CUBEMAP) {}
+};
+
+class TextureCubemapArrayRD : public TextureLayeredRD {
+ GDCLASS(TextureCubemapArrayRD, TextureLayeredRD)
+
+public:
+ TextureCubemapArrayRD() :
+ TextureLayeredRD(LAYERED_TYPE_CUBEMAP_ARRAY) {}
+};
+
+class Texture3DRD : public Texture3D {
+ GDCLASS(Texture3DRD, Texture3D)
+
+ mutable RID texture_rid;
+ RID texture_rd_rid;
+
+ Image::Format image_format;
+ Vector3i size;
+ uint32_t mipmaps = 0;
+
+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_depth() const override;
+ virtual bool has_mipmaps() const override;
+ virtual RID get_rid() const override;
+
+ void set_texture_rd_rid(RID p_texture_rd_rid);
+ RID get_texture_rd_rid() const;
+
+ Texture3DRD();
+ ~Texture3DRD();
+};
+
+#endif // TEXTURE_RD_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 d74809a512..f340573c6a 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();
@@ -3948,6 +3948,9 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value)
} else if (components[1] == "animation_speed") {
set_tile_animation_speed(coords, p_value);
return true;
+ } else if (components[1] == "animation_mode") {
+ set_tile_animation_mode(coords, VariantCaster<TileSetAtlasSource::TileAnimationMode>::cast(p_value));
+ return true;
} else if (components[1] == "animation_frames_count") {
set_tile_animation_frames_count(coords, p_value);
return true;
@@ -4015,6 +4018,9 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const {
} else if (components[1] == "animation_speed") {
r_ret = get_tile_animation_speed(coords);
return true;
+ } else if (components[1] == "animation_mode") {
+ r_ret = get_tile_animation_mode(coords);
+ return true;
} else if (components[1] == "animation_frames_count") {
r_ret = get_tile_animation_frames_count(coords);
return true;
@@ -4090,6 +4096,13 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
}
tile_property_list.push_back(property_info);
+ // animation_mode.
+ property_info = PropertyInfo(Variant::INT, "animation_mode", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
+ if (E_tile.value.animation_mode == TILE_ANIMATION_MODE_DEFAULT) {
+ property_info.usage ^= PROPERTY_USAGE_STORAGE;
+ }
+ tile_property_list.push_back(property_info);
+
// animation_frames_count.
tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
@@ -4252,6 +4265,20 @@ real_t TileSetAtlasSource::get_tile_animation_speed(const Vector2i p_atlas_coord
return tiles[p_atlas_coords].animation_speed;
}
+void TileSetAtlasSource::set_tile_animation_mode(const Vector2i p_atlas_coords, TileSetAtlasSource::TileAnimationMode p_mode) {
+ ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+
+ tiles[p_atlas_coords].animation_mode = p_mode;
+
+ emit_signal(SNAME("changed"));
+}
+
+TileSetAtlasSource::TileAnimationMode TileSetAtlasSource::get_tile_animation_mode(const Vector2i p_atlas_coords) const {
+ ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TILE_ANIMATION_MODE_DEFAULT, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
+
+ return tiles[p_atlas_coords].animation_mode;
+}
+
void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count) {
ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords)));
ERR_FAIL_COND(p_frames_count < 1);
@@ -4577,6 +4604,8 @@ void TileSetAtlasSource::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tile_animation_separation", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_separation);
ClassDB::bind_method(D_METHOD("set_tile_animation_speed", "atlas_coords", "speed"), &TileSetAtlasSource::set_tile_animation_speed);
ClassDB::bind_method(D_METHOD("get_tile_animation_speed", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_speed);
+ ClassDB::bind_method(D_METHOD("set_tile_animation_mode", "atlas_coords", "mode"), &TileSetAtlasSource::set_tile_animation_mode);
+ ClassDB::bind_method(D_METHOD("get_tile_animation_mode", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_mode);
ClassDB::bind_method(D_METHOD("set_tile_animation_frames_count", "atlas_coords", "frames_count"), &TileSetAtlasSource::set_tile_animation_frames_count);
ClassDB::bind_method(D_METHOD("get_tile_animation_frames_count", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_frames_count);
ClassDB::bind_method(D_METHOD("set_tile_animation_frame_duration", "atlas_coords", "frame_index", "duration"), &TileSetAtlasSource::set_tile_animation_frame_duration);
@@ -4599,6 +4628,10 @@ void TileSetAtlasSource::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_padded_texture"), &TileSetAtlasSource::_update_padded_texture);
ClassDB::bind_method(D_METHOD("get_runtime_texture"), &TileSetAtlasSource::get_runtime_texture);
ClassDB::bind_method(D_METHOD("get_runtime_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_runtime_tile_texture_region);
+
+ BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_DEFAULT)
+ BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_RANDOM_START_TIMES)
+ BIND_ENUM_CONSTANT(TILE_ANIMATION_MODE_MAX)
}
TileSetAtlasSource::~TileSetAtlasSource() {
@@ -5083,6 +5116,8 @@ void TileData::remove_terrain(int p_terrain_set, int p_index) {
if (terrain_set == p_terrain_set) {
if (terrain == p_index) {
terrain = -1;
+ } else if (terrain > p_index) {
+ terrain -= 1;
}
for (int i = 0; i < 16; i++) {
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 337a945e7b..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;
@@ -592,6 +592,13 @@ public:
class TileSetAtlasSource : public TileSetSource {
GDCLASS(TileSetAtlasSource, TileSetSource);
+public:
+ enum TileAnimationMode {
+ TILE_ANIMATION_MODE_DEFAULT,
+ TILE_ANIMATION_MODE_RANDOM_START_TIMES,
+ TILE_ANIMATION_MODE_MAX,
+ };
+
private:
struct TileAlternativesData {
Vector2i size_in_atlas = Vector2i(1, 1);
@@ -601,6 +608,7 @@ private:
int animation_columns = 0;
Vector2i animation_separation;
real_t animation_speed = 1.0;
+ TileSetAtlasSource::TileAnimationMode animation_mode = TILE_ANIMATION_MODE_DEFAULT;
LocalVector<real_t> animation_frames_durations;
// Alternatives
@@ -701,6 +709,8 @@ public:
Vector2i get_tile_animation_separation(const Vector2i p_atlas_coords) const;
void set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed);
real_t get_tile_animation_speed(const Vector2i p_atlas_coords) const;
+ void set_tile_animation_mode(const Vector2i p_atlas_coords, const TileSetAtlasSource::TileAnimationMode p_mode);
+ TileSetAtlasSource::TileAnimationMode get_tile_animation_mode(const Vector2i p_atlas_coords) const;
void set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count);
int get_tile_animation_frames_count(const Vector2i p_atlas_coords) const;
void set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration);
@@ -932,4 +942,6 @@ VARIANT_ENUM_CAST(TileSet::TileShape);
VARIANT_ENUM_CAST(TileSet::TileLayout);
VARIANT_ENUM_CAST(TileSet::TileOffsetAxis);
+VARIANT_ENUM_CAST(TileSetAtlasSource::TileAnimationMode);
+
#endif // TILE_SET_H
diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp
index ee1a47c338..dc8545426f 100644
--- a/scene/resources/video_stream.cpp
+++ b/scene/resources/video_stream.cpp
@@ -75,7 +75,7 @@ bool VideoStreamPlayback::is_playing() const {
}
void VideoStreamPlayback::set_paused(bool p_paused) {
- GDVIRTUAL_CALL(_is_playing, p_paused);
+ GDVIRTUAL_CALL(_set_paused, p_paused);
}
bool VideoStreamPlayback::is_paused() const {
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 71e84ef636..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();
@@ -3645,6 +3658,8 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha AA Edge", "ALPHA_ANTIALIASING_EDGE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Alpha UV", "ALPHA_TEXTURE_COORDINATE" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Depth", "DEPTH" },
+
////////////////////////////////////////////////////////////////////////
// Node3D, Light.
////////////////////////////////////////////////////////////////////////
@@ -3768,7 +3783,7 @@ bool VisualShaderNodeOutput::is_port_separator(int p_index) const {
}
if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) {
String port_name = get_input_port_name(p_index);
- return bool(port_name == "AO" || port_name == "Normal" || port_name == "Rim" || port_name == "Clearcoat" || port_name == "Anisotropy" || port_name == "Subsurf Scatter" || port_name == "Alpha Scissor Threshold");
+ return bool(port_name == "AO" || port_name == "Normal" || port_name == "Rim" || port_name == "Clearcoat" || port_name == "Anisotropy" || port_name == "Subsurf Scatter" || port_name == "Alpha Scissor Threshold" || port_name == "Depth");
}
return false;
}
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..4023de9023 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 {
@@ -1725,6 +1727,135 @@ VisualShaderNodeLinearSceneDepth::VisualShaderNodeLinearSceneDepth() {
simple_decl = false;
}
+////////////// World Position from Depth
+
+String VisualShaderNodeWorldPositionFromDepth::get_caption() const {
+ return "WorldPositionFromDepth";
+}
+
+int VisualShaderNodeWorldPositionFromDepth::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeWorldPositionFromDepth::PortType VisualShaderNodeWorldPositionFromDepth::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_2D;
+}
+
+String VisualShaderNodeWorldPositionFromDepth::get_input_port_name(int p_port) const {
+ return "screen uv";
+}
+
+bool VisualShaderNodeWorldPositionFromDepth::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
+}
+
+int VisualShaderNodeWorldPositionFromDepth::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeWorldPositionFromDepth::PortType VisualShaderNodeWorldPositionFromDepth::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_3D;
+}
+
+String VisualShaderNodeWorldPositionFromDepth::get_output_port_name(int p_port) const {
+ return "world position";
+}
+
+bool VisualShaderNodeWorldPositionFromDepth::has_output_port_preview(int p_port) const {
+ return false;
+}
+
+String VisualShaderNodeWorldPositionFromDepth::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform sampler2D " + make_unique_id(p_type, p_id, "depth_tex") + " : hint_depth_texture, repeat_disable, filter_nearest;\n";
+}
+
+String VisualShaderNodeWorldPositionFromDepth::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ String code;
+ String uv = p_input_vars[0].is_empty() ? "SCREEN_UV" : p_input_vars[0];
+ code += " {\n";
+
+ code += " float __log_depth = textureLod(" + make_unique_id(p_type, p_id, "depth_tex") + ", " + uv + ", 0.0).x;\n";
+ if (!RenderingServer::get_singleton()->is_low_end()) {
+ code += " vec4 __depth_view = INV_PROJECTION_MATRIX * vec4(" + uv + " * 2.0 - 1.0, __log_depth, 1.0);\n";
+ } else {
+ code += " vec4 __depth_view = INV_PROJECTION_MATRIX * vec4(vec3(" + uv + ", __log_depth) * 2.0 - 1.0, 1.0);\n";
+ }
+ code += " __depth_view.xyz /= __depth_view.w;\n";
+ code += vformat(" %s = (INV_VIEW_MATRIX * __depth_view).xyz;\n", p_output_vars[0]);
+
+ code += " }\n";
+ return code;
+}
+
+VisualShaderNodeWorldPositionFromDepth::VisualShaderNodeWorldPositionFromDepth() {
+ simple_decl = false;
+}
+
+////////////// Unpack Normals in World Space
+
+String VisualShaderNodeScreenNormalWorldSpace::get_caption() const {
+ return "ScreenNormalWorldSpace";
+}
+
+int VisualShaderNodeScreenNormalWorldSpace::get_input_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeScreenNormalWorldSpace::PortType VisualShaderNodeScreenNormalWorldSpace::get_input_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_2D;
+}
+
+String VisualShaderNodeScreenNormalWorldSpace::get_input_port_name(int p_port) const {
+ return "screen uv";
+}
+
+bool VisualShaderNodeScreenNormalWorldSpace::is_input_port_default(int p_port, Shader::Mode p_mode) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
+}
+
+int VisualShaderNodeScreenNormalWorldSpace::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeScreenNormalWorldSpace::PortType VisualShaderNodeScreenNormalWorldSpace::get_output_port_type(int p_port) const {
+ return PORT_TYPE_VECTOR_3D;
+}
+
+String VisualShaderNodeScreenNormalWorldSpace::get_output_port_name(int p_port) const {
+ return "screen normal";
+}
+
+bool VisualShaderNodeScreenNormalWorldSpace::has_output_port_preview(int p_port) const {
+ return false;
+}
+
+String VisualShaderNodeScreenNormalWorldSpace::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+ return "uniform sampler2D " + make_unique_id(p_type, p_id, "normal_rough_tex") + " : hint_normal_roughness_texture, repeat_disable, filter_nearest;\n";
+}
+
+String VisualShaderNodeScreenNormalWorldSpace::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ String code;
+ String uv = p_input_vars[0].is_empty() ? "SCREEN_UV" : p_input_vars[0];
+ code += " {\n";
+
+ code += " vec3 __normals = textureLod(" + make_unique_id(p_type, p_id, "normal_rough_tex") + ", " + uv + ", 0.0).xyz;\n";
+ code += " __normals = __normals * 2.0 - 1.0;\n";
+ code += vformat(" %s = mat3(INV_VIEW_MATRIX) * __normals;\n", p_output_vars[0]);
+
+ code += " }\n";
+ return code;
+}
+
+VisualShaderNodeScreenNormalWorldSpace::VisualShaderNodeScreenNormalWorldSpace() {
+ simple_decl = false;
+}
+
////////////// Float Op
String VisualShaderNodeFloatOp::get_caption() const {
@@ -4135,6 +4266,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 +4425,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;
}
@@ -7961,3 +8100,100 @@ VisualShaderNodeRemap::VisualShaderNodeRemap() {
simple_decl = false;
}
+
+////////////// RotationByAxis
+
+String VisualShaderNodeRotationByAxis::get_caption() const {
+ return "RotationByAxis";
+}
+
+int VisualShaderNodeRotationByAxis::get_input_port_count() const {
+ return 3;
+}
+
+VisualShaderNodeRotationByAxis::PortType VisualShaderNodeRotationByAxis::get_input_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return PORT_TYPE_VECTOR_3D;
+ case 1:
+ return PORT_TYPE_SCALAR;
+ case 2:
+ return PORT_TYPE_VECTOR_3D;
+ default:
+ break;
+ }
+
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeRotationByAxis::get_input_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "input";
+ case 1:
+ return "angle";
+ case 2:
+ return "axis";
+ default:
+ break;
+ }
+
+ return "";
+}
+
+int VisualShaderNodeRotationByAxis::get_output_port_count() const {
+ return 2;
+}
+
+VisualShaderNodeRotationByAxis::PortType VisualShaderNodeRotationByAxis::get_output_port_type(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return PORT_TYPE_VECTOR_3D;
+ case 1:
+ return PORT_TYPE_TRANSFORM;
+ default:
+ break;
+ }
+
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeRotationByAxis::get_output_port_name(int p_port) const {
+ switch (p_port) {
+ case 0:
+ return "output";
+ case 1:
+ return "rotationMat";
+ default:
+ break;
+ }
+
+ return "";
+}
+
+bool VisualShaderNodeRotationByAxis::has_output_port_preview(int p_port) const {
+ return false;
+}
+
+String VisualShaderNodeRotationByAxis::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ String code;
+ code += " {\n";
+ code += vformat(" float __angle = %s;\n", p_input_vars[1]);
+ code += vformat(" vec3 __axis = normalize(%s);\n", p_input_vars[2]);
+ code += vformat(" mat3 __rot_matrix = mat3(\n");
+ code += vformat(" vec3( cos(__angle)+__axis.x*__axis.x*(1.0 - cos(__angle)), __axis.x*__axis.y*(1.0-cos(__angle))-__axis.z*sin(__angle), __axis.x*__axis.z*(1.0-cos(__angle))+__axis.y*sin(__angle) ),\n");
+ code += vformat(" vec3( __axis.y*__axis.x*(1.0-cos(__angle))+__axis.z*sin(__angle), cos(__angle)+__axis.y*__axis.y*(1.0-cos(__angle)), __axis.y*__axis.z*(1.0-cos(__angle))-__axis.x*sin(__angle) ),\n");
+ code += vformat(" vec3( __axis.z*__axis.x*(1.0-cos(__angle))-__axis.y*sin(__angle), __axis.z*__axis.y*(1.0-cos(__angle))+__axis.x*sin(__angle), cos(__angle)+__axis.z*__axis.z*(1.0-cos(__angle)) )\n");
+ code += vformat(" );\n");
+ code += vformat(" %s = %s * __rot_matrix;\n", p_output_vars[0], p_input_vars[0]);
+ code += vformat(" %s = mat4(__rot_matrix);\n", p_output_vars[1]);
+ code += " }\n";
+ return code;
+}
+
+VisualShaderNodeRotationByAxis::VisualShaderNodeRotationByAxis() {
+ set_input_port_default_value(1, 0.0);
+ set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0));
+
+ simple_decl = false;
+}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index fa6b134526..97d8df3c0b 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
///////////////////////////////////////
@@ -676,6 +680,50 @@ public:
VisualShaderNodeLinearSceneDepth();
};
+class VisualShaderNodeWorldPositionFromDepth : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeWorldPositionFromDepth, VisualShaderNode);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ VisualShaderNodeWorldPositionFromDepth();
+};
+
+class VisualShaderNodeScreenNormalWorldSpace : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeScreenNormalWorldSpace, VisualShaderNode);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+ virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
+
+ virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ VisualShaderNodeScreenNormalWorldSpace();
+};
+
///////////////////////////////////////
/// OPS
///////////////////////////////////////
@@ -1657,6 +1705,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 +1752,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;
@@ -2907,4 +2957,24 @@ public:
VisualShaderNodeRemap();
};
+class VisualShaderNodeRotationByAxis : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeRotationByAxis, VisualShaderNode);
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ VisualShaderNodeRotationByAxis();
+};
+
#endif // VISUAL_SHADER_NODES_H
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_3d.cpp b/scene/resources/world_3d.cpp
index 297d219caf..b8646c5387 100644
--- a/scene/resources/world_3d.cpp
+++ b/scene/resources/world_3d.cpp
@@ -68,6 +68,7 @@ RID World3D::get_navigation_map() const {
NavigationServer3D::get_singleton()->map_set_active(navigation_map, true);
NavigationServer3D::get_singleton()->map_set_cell_size(navigation_map, GLOBAL_GET("navigation/3d/default_cell_size"));
NavigationServer3D::get_singleton()->map_set_cell_height(navigation_map, GLOBAL_GET("navigation/3d/default_cell_height"));
+ NavigationServer3D::get_singleton()->map_set_up(navigation_map, GLOBAL_GET("navigation/3d/default_up"));
NavigationServer3D::get_singleton()->map_set_use_edge_connections(navigation_map, GLOBAL_GET("navigation/3d/use_edge_connections"));
NavigationServer3D::get_singleton()->map_set_edge_connection_margin(navigation_map, GLOBAL_GET("navigation/3d/default_edge_connection_margin"));
NavigationServer3D::get_singleton()->map_set_link_connection_radius(navigation_map, GLOBAL_GET("navigation/3d/default_link_connection_radius"));
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 41fa0d2d47..f41238b075 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -362,10 +362,18 @@ String DisplayServer::clipboard_get() const {
ERR_FAIL_V_MSG(String(), "Clipboard is not supported by this display server.");
}
+Ref<Image> DisplayServer::clipboard_get_image() const {
+ ERR_FAIL_V_MSG(Ref<Image>(), "Clipboard is not supported by this display server.");
+}
+
bool DisplayServer::clipboard_has() const {
return !clipboard_get().is_empty();
}
+bool DisplayServer::clipboard_has_image() const {
+ return clipboard_get_image().is_valid();
+}
+
void DisplayServer::clipboard_set_primary(const String &p_text) {
WARN_PRINT("Primary clipboard is not supported by this display server.");
}
@@ -497,6 +505,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;
}
@@ -520,6 +533,10 @@ Key DisplayServer::keyboard_get_keycode_from_physical(Key p_keycode) const {
ERR_FAIL_V_MSG(p_keycode, "Not supported by this display server.");
}
+Key DisplayServer::keyboard_get_label_from_physical(Key p_keycode) const {
+ ERR_FAIL_V_MSG(p_keycode, "Not supported by this display server.");
+}
+
void DisplayServer::force_process_and_drop_events() {
}
@@ -640,7 +657,9 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("clipboard_set", "clipboard"), &DisplayServer::clipboard_set);
ClassDB::bind_method(D_METHOD("clipboard_get"), &DisplayServer::clipboard_get);
+ ClassDB::bind_method(D_METHOD("clipboard_get_image"), &DisplayServer::clipboard_get_image);
ClassDB::bind_method(D_METHOD("clipboard_has"), &DisplayServer::clipboard_has);
+ ClassDB::bind_method(D_METHOD("clipboard_has_image"), &DisplayServer::clipboard_has_image);
ClassDB::bind_method(D_METHOD("clipboard_set_primary", "clipboard_primary"), &DisplayServer::clipboard_set_primary);
ClassDB::bind_method(D_METHOD("clipboard_get_primary"), &DisplayServer::clipboard_get_primary);
@@ -751,12 +770,15 @@ 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);
ClassDB::bind_method(D_METHOD("keyboard_get_layout_language", "index"), &DisplayServer::keyboard_get_layout_language);
ClassDB::bind_method(D_METHOD("keyboard_get_layout_name", "index"), &DisplayServer::keyboard_get_layout_name);
ClassDB::bind_method(D_METHOD("keyboard_get_keycode_from_physical", "keycode"), &DisplayServer::keyboard_get_keycode_from_physical);
+ ClassDB::bind_method(D_METHOD("keyboard_get_label_from_physical", "keycode"), &DisplayServer::keyboard_get_label_from_physical);
ClassDB::bind_method(D_METHOD("process_events"), &DisplayServer::process_events);
ClassDB::bind_method(D_METHOD("force_process_and_drop_events"), &DisplayServer::force_process_and_drop_events);
@@ -841,6 +863,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 fc8207f2d3..85f9270696 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -245,7 +245,9 @@ public:
virtual void clipboard_set(const String &p_text);
virtual String clipboard_get() const;
+ virtual Ref<Image> clipboard_get_image() const;
virtual bool clipboard_has() const;
+ virtual bool clipboard_has_image() const;
virtual void clipboard_set_primary(const String &p_text);
virtual String clipboard_get_primary() const;
@@ -493,12 +495,22 @@ 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);
virtual String keyboard_get_layout_language(int p_index) const;
virtual String keyboard_get_layout_name(int p_index) const;
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const;
+ virtual Key keyboard_get_label_from_physical(Key p_keycode) const;
virtual int tablet_get_driver_count() const { return 1; };
virtual String tablet_get_driver_name(int p_driver) const { return "default"; };
@@ -545,5 +557,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_2d.cpp b/servers/navigation_server_2d.cpp
index e906db2acf..cd92d9dd2f 100644
--- a/servers/navigation_server_2d.cpp
+++ b/servers/navigation_server_2d.cpp
@@ -391,6 +391,8 @@ void NavigationServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer2D::query_path);
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer2D::region_create);
+ ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer2D::region_set_enabled);
+ ClassDB::bind_method(D_METHOD("region_get_enabled", "region"), &NavigationServer2D::region_get_enabled);
ClassDB::bind_method(D_METHOD("region_set_use_edge_connections", "region", "enabled"), &NavigationServer2D::region_set_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_get_use_edge_connections", "region"), &NavigationServer2D::region_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_set_enter_cost", "region", "enter_cost"), &NavigationServer2D::region_set_enter_cost);
@@ -413,6 +415,8 @@ void NavigationServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create);
ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer2D::link_set_map);
ClassDB::bind_method(D_METHOD("link_get_map", "link"), &NavigationServer2D::link_get_map);
+ ClassDB::bind_method(D_METHOD("link_set_enabled", "link", "enabled"), &NavigationServer2D::link_set_enabled);
+ ClassDB::bind_method(D_METHOD("link_get_enabled", "link"), &NavigationServer2D::link_get_enabled);
ClassDB::bind_method(D_METHOD("link_set_bidirectional", "link", "bidirectional"), &NavigationServer2D::link_set_bidirectional);
ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer2D::link_is_bidirectional);
ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer2D::link_set_navigation_layers);
@@ -536,6 +540,8 @@ RID FORWARD_2_C(map_get_closest_point_owner, RID, p_map, const Vector2 &, p_poin
RID FORWARD_0(region_create);
+void FORWARD_2(region_set_enabled, RID, p_region, bool, p_enabled, rid_to_rid, bool_to_bool);
+bool FORWARD_1_C(region_get_enabled, RID, p_region, rid_to_rid);
void FORWARD_2(region_set_use_edge_connections, RID, p_region, bool, p_enabled, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(region_get_use_edge_connections, RID, p_region, rid_to_rid);
@@ -564,6 +570,8 @@ RID FORWARD_0(link_create);
void FORWARD_2(link_set_map, RID, p_link, RID, p_map, rid_to_rid, rid_to_rid);
RID FORWARD_1_C(link_get_map, RID, p_link, rid_to_rid);
+void FORWARD_2(link_set_enabled, RID, p_link, bool, p_enabled, rid_to_rid, bool_to_bool);
+bool FORWARD_1_C(link_get_enabled, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional, rid_to_rid, bool_to_bool);
bool FORWARD_1_C(link_is_bidirectional, RID, p_link, rid_to_rid);
void FORWARD_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers, rid_to_rid, uint32_to_uint32);
diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h
index b9b1e2a75e..c78edaf878 100644
--- a/servers/navigation_server_2d.h
+++ b/servers/navigation_server_2d.h
@@ -101,6 +101,9 @@ public:
/// Creates a new region.
virtual RID region_create();
+ virtual void region_set_enabled(RID p_region, bool p_enabled);
+ virtual bool region_get_enabled(RID p_region) const;
+
virtual void region_set_use_edge_connections(RID p_region, bool p_enabled);
virtual bool region_get_use_edge_connections(RID p_region) const;
@@ -144,6 +147,9 @@ public:
virtual void link_set_map(RID p_link, RID p_map);
virtual RID link_get_map(RID p_link) const;
+ virtual void link_set_enabled(RID p_link, bool p_enabled);
+ virtual bool link_get_enabled(RID p_link) const;
+
/// Set whether this link travels in both directions.
virtual void link_set_bidirectional(RID p_link, bool p_bidirectional);
virtual bool link_is_bidirectional(RID p_link) const;
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index dae8ea6dcd..04facdb8d9 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -67,6 +67,8 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("query_path", "parameters", "result"), &NavigationServer3D::query_path);
ClassDB::bind_method(D_METHOD("region_create"), &NavigationServer3D::region_create);
+ ClassDB::bind_method(D_METHOD("region_set_enabled", "region", "enabled"), &NavigationServer3D::region_set_enabled);
+ ClassDB::bind_method(D_METHOD("region_get_enabled", "region"), &NavigationServer3D::region_get_enabled);
ClassDB::bind_method(D_METHOD("region_set_use_edge_connections", "region", "enabled"), &NavigationServer3D::region_set_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_get_use_edge_connections", "region"), &NavigationServer3D::region_get_use_edge_connections);
ClassDB::bind_method(D_METHOD("region_set_enter_cost", "region", "enter_cost"), &NavigationServer3D::region_set_enter_cost);
@@ -82,7 +84,9 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer3D::region_get_navigation_layers);
ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer3D::region_set_transform);
ClassDB::bind_method(D_METHOD("region_set_navigation_mesh", "region", "navigation_mesh"), &NavigationServer3D::region_set_navigation_mesh);
+#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("region_bake_navigation_mesh", "navigation_mesh", "root_node"), &NavigationServer3D::region_bake_navigation_mesh);
+#endif // DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer3D::region_get_connections_count);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_start);
ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_end);
@@ -90,6 +94,8 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer3D::link_create);
ClassDB::bind_method(D_METHOD("link_set_map", "link", "map"), &NavigationServer3D::link_set_map);
ClassDB::bind_method(D_METHOD("link_get_map", "link"), &NavigationServer3D::link_get_map);
+ ClassDB::bind_method(D_METHOD("link_set_enabled", "link", "enabled"), &NavigationServer3D::link_set_enabled);
+ ClassDB::bind_method(D_METHOD("link_get_enabled", "link"), &NavigationServer3D::link_get_enabled);
ClassDB::bind_method(D_METHOD("link_set_bidirectional", "link", "bidirectional"), &NavigationServer3D::link_set_bidirectional);
ClassDB::bind_method(D_METHOD("link_is_bidirectional", "link"), &NavigationServer3D::link_is_bidirectional);
ClassDB::bind_method(D_METHOD("link_set_navigation_layers", "link", "navigation_layers"), &NavigationServer3D::link_set_navigation_layers);
@@ -183,13 +189,14 @@ 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);
+ GLOBAL_DEF("navigation/3d/default_up", Vector3(0, 1, 0));
GLOBAL_DEF("navigation/3d/use_edge_connections", true);
GLOBAL_DEF_BASIC("navigation/3d/default_edge_connection_margin", 0.25);
GLOBAL_DEF_BASIC("navigation/3d/default_link_connection_radius", 1.0);
diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h
index cf5552e676..391730e18f 100644
--- a/servers/navigation_server_3d.h
+++ b/servers/navigation_server_3d.h
@@ -117,6 +117,9 @@ public:
/// Creates a new region.
virtual RID region_create() = 0;
+ virtual void region_set_enabled(RID p_region, bool p_enabled) = 0;
+ virtual bool region_get_enabled(RID p_region) const = 0;
+
virtual void region_set_use_edge_connections(RID p_region, bool p_enabled) = 0;
virtual bool region_get_use_edge_connections(RID p_region) const = 0;
@@ -148,8 +151,10 @@ public:
/// Set the navigation mesh of this region.
virtual void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) = 0;
+#ifndef DISABLE_DEPRECATED
/// Bake the navigation mesh.
virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) = 0;
+#endif // DISABLE_DEPRECATED
/// Get a list of a region's connection to other regions.
virtual int region_get_connections_count(RID p_region) const = 0;
@@ -163,6 +168,9 @@ public:
virtual void link_set_map(RID p_link, RID p_map) = 0;
virtual RID link_get_map(RID p_link) const = 0;
+ virtual void link_set_enabled(RID p_link, bool p_enabled) = 0;
+ virtual bool link_get_enabled(RID p_link) const = 0;
+
/// Set whether this link travels in both directions.
virtual void link_set_bidirectional(RID p_link, bool p_bidirectional) = 0;
virtual bool link_is_bidirectional(RID p_link) const = 0;
diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h
index 8e91ec812f..b2d452f67a 100644
--- a/servers/navigation_server_3d_dummy.h
+++ b/servers/navigation_server_3d_dummy.h
@@ -64,6 +64,8 @@ public:
TypedArray<RID> map_get_obstacles(RID p_map) const override { return TypedArray<RID>(); }
void map_force_update(RID p_map) override {}
RID region_create() override { return RID(); }
+ void region_set_enabled(RID p_region, bool p_enabled) override {}
+ bool region_get_enabled(RID p_region) const override { return false; }
void region_set_use_edge_connections(RID p_region, bool p_enabled) override {}
bool region_get_use_edge_connections(RID p_region) const override { return false; }
void region_set_enter_cost(RID p_region, real_t p_enter_cost) override {}
@@ -79,13 +81,17 @@ public:
uint32_t region_get_navigation_layers(RID p_region) const override { return 0; }
void region_set_transform(RID p_region, Transform3D p_transform) override {}
void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) override {}
+#ifndef DISABLE_DEPRECATED
void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) override {}
+#endif // DISABLE_DEPRECATED
int region_get_connections_count(RID p_region) const override { return 0; }
Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector3(); }
Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector3(); }
RID link_create() override { return RID(); }
void link_set_map(RID p_link, RID p_map) override {}
RID link_get_map(RID p_link) const override { return RID(); }
+ void link_set_enabled(RID p_link, bool p_enabled) override {}
+ bool link_get_enabled(RID p_link) const override { return false; }
void link_set_bidirectional(RID p_link, bool p_bidirectional) override {}
bool link_is_bidirectional(RID p_link) const override { return false; }
void link_set_navigation_layers(RID p_link, uint32_t p_navigation_layers) override {}
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 7cc9a82699..67e48df9c9 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -69,8 +69,10 @@
#include "physics_server_3d.h"
#include "physics_server_3d_wrap_mt.h"
#include "rendering/renderer_compositor.h"
+#include "rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h"
#include "rendering/rendering_device.h"
#include "rendering/rendering_device_binds.h"
+#include "rendering/storage/render_scene_buffers.h"
#include "rendering_server.h"
#include "servers/extensions/physics_server_2d_extension.h"
#include "servers/extensions/physics_server_3d_extension.h"
@@ -247,6 +249,11 @@ void register_server_types() {
GDREGISTER_CLASS(RDShaderFile);
GDREGISTER_CLASS(RDPipelineSpecializationConstant);
+ GDREGISTER_CLASS(RenderSceneBuffersConfiguration);
+ GDREGISTER_ABSTRACT_CLASS(RenderSceneBuffers);
+ GDREGISTER_CLASS(RenderSceneBuffersExtension);
+ GDREGISTER_ABSTRACT_CLASS(RenderSceneBuffersRD);
+
GDREGISTER_CLASS(CameraFeed);
GDREGISTER_ABSTRACT_CLASS(PhysicsDirectBodyState2D);
diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h
index 768b1ba702..71a1801de9 100644
--- a/servers/rendering/dummy/storage/texture_storage.h
+++ b/servers/rendering/dummy/storage/texture_storage.h
@@ -117,6 +117,8 @@ public:
virtual void texture_set_path(RID p_texture, const String &p_path) override{};
virtual String texture_get_path(RID p_texture) const override { return String(); };
+ virtual Image::Format texture_get_format(RID p_texture) const override { return Image::FORMAT_MAX; }
+
virtual void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override{};
virtual void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override{};
virtual void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) override{};
@@ -127,6 +129,7 @@ public:
virtual Size2 texture_size_with_proxy(RID p_proxy) override { return Size2(); };
+ virtual void texture_rd_initialize(RID p_texture, const RID &p_rd_texture, const RS::TextureLayeredType p_layer_type = RS::TEXTURE_LAYERED_2D_ARRAY) override{};
virtual RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) const override { return RID(); };
virtual uint64_t texture_get_native_handle(RID p_texture, bool p_srgb = false) const override { return 0; };
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/effects/debug_effects.cpp b/servers/rendering/renderer_rd/effects/debug_effects.cpp
new file mode 100644
index 0000000000..8cd3c22483
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/debug_effects.cpp
@@ -0,0 +1,328 @@
+/**************************************************************************/
+/* debug_effects.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 "debug_effects.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+#include "servers/rendering/renderer_rd/storage_rd/light_storage.h"
+#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
+#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
+
+using namespace RendererRD;
+
+DebugEffects::DebugEffects() {
+ {
+ // Shadow Frustum debug shader
+ Vector<String> modes;
+ modes.push_back("");
+
+ shadow_frustum.shader.initialize(modes);
+ shadow_frustum.shader_version = shadow_frustum.shader.version_create();
+
+ RD::PipelineRasterizationState raster_state = RD::PipelineRasterizationState();
+ shadow_frustum.pipelines[SFP_TRANSPARENT].setup(shadow_frustum.shader.version_get_shader(shadow_frustum.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, raster_state, RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_blend(), 0);
+
+ raster_state.wireframe = true;
+ shadow_frustum.pipelines[SFP_WIREFRAME].setup(shadow_frustum.shader.version_get_shader(shadow_frustum.shader_version, 0), RD::RENDER_PRIMITIVE_LINES, raster_state, RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0);
+ }
+}
+
+void DebugEffects::_create_frustum_arrays() {
+ if (frustum.vertex_buffer.is_null()) {
+ // Create vertex buffer, but don't put data in it yet
+ frustum.vertex_buffer = RD::get_singleton()->vertex_buffer_create(8 * sizeof(float) * 3, Vector<uint8_t>(), false);
+
+ Vector<RD::VertexAttribute> attributes;
+ Vector<RID> buffers;
+ RD::VertexAttribute vd;
+
+ vd.location = 0;
+ vd.stride = sizeof(float) * 3;
+ vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
+
+ attributes.push_back(vd);
+ buffers.push_back(frustum.vertex_buffer);
+
+ frustum.vertex_format = RD::get_singleton()->vertex_format_create(attributes);
+ frustum.vertex_array = RD::get_singleton()->vertex_array_create(8, frustum.vertex_format, buffers);
+ }
+
+ if (frustum.index_buffer.is_null()) {
+ uint32_t indices[6 * 2 * 3] = {
+ // Far
+ 0, 1, 2, // FLT, FLB, FRT
+ 1, 3, 2, // FLB, FRB, FRT
+ // Near
+ 4, 6, 5, // NLT, NRT, NLB
+ 6, 7, 5, // NRT, NRB, NLB
+ // Left
+ 0, 4, 1, // FLT, NLT, FLB
+ 4, 5, 1, // NLT, NLB, FLB
+ // Right
+ 6, 2, 7, // NRT, FRT, NRB
+ 2, 3, 7, // FRT, FRB, NRB
+ // Top
+ 0, 2, 4, // FLT, FRT, NLT
+ 2, 6, 4, // FRT, NRT, NLT
+ // Bottom
+ 5, 7, 1, // NLB, NRB, FLB,
+ 7, 3, 1, // NRB, FRB, FLB
+ };
+
+ // Create our index_array
+ PackedByteArray data;
+ data.resize(6 * 2 * 3 * 4);
+ {
+ uint8_t *w = data.ptrw();
+ int *p32 = (int *)w;
+ for (int i = 0; i < 6 * 2 * 3; i++) {
+ *p32 = indices[i];
+ p32++;
+ }
+ }
+
+ frustum.index_buffer = RD::get_singleton()->index_buffer_create(6 * 2 * 3, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, data);
+ frustum.index_array = RD::get_singleton()->index_array_create(frustum.index_buffer, 0, 6 * 2 * 3);
+ }
+
+ if (frustum.lines_buffer.is_null()) {
+ uint32_t indices[12 * 2] = {
+ 0, 1, // FLT - FLB
+ 1, 3, // FLB - FRB
+ 3, 2, // FRB - FRT
+ 2, 0, // FRT - FLT
+
+ 4, 6, // NLT - NRT
+ 6, 7, // NRT - NRB
+ 7, 5, // NRB - NLB
+ 5, 4, // NLB - NLT
+
+ 0, 4, // FLT - NLT
+ 1, 5, // FLB - NLB
+ 2, 6, // FRT - NRT
+ 3, 7, // FRB - NRB
+ };
+
+ // Create our lines_array
+ PackedByteArray data;
+ data.resize(12 * 2 * 4);
+ {
+ uint8_t *w = data.ptrw();
+ int *p32 = (int *)w;
+ for (int i = 0; i < 12 * 2; i++) {
+ *p32 = indices[i];
+ p32++;
+ }
+ }
+
+ frustum.lines_buffer = RD::get_singleton()->index_buffer_create(12 * 2, RenderingDevice::INDEX_BUFFER_FORMAT_UINT32, data);
+ frustum.lines_array = RD::get_singleton()->index_array_create(frustum.lines_buffer, 0, 12 * 2);
+ }
+}
+
+DebugEffects::~DebugEffects() {
+ shadow_frustum.shader.version_free(shadow_frustum.shader_version);
+
+ // Destroy vertex buffer and array.
+ if (frustum.vertex_buffer.is_valid()) {
+ RD::get_singleton()->free(frustum.vertex_buffer); // Array gets freed as dependency.
+ }
+
+ // Destroy index buffer and array,
+ if (frustum.index_buffer.is_valid()) {
+ RD::get_singleton()->free(frustum.index_buffer); // Array gets freed as dependency.
+ }
+
+ // Destroy lines buffer and array.
+ if (frustum.lines_buffer.is_valid()) {
+ RD::get_singleton()->free(frustum.lines_buffer); // Array gets freed as dependency.
+ }
+}
+
+void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect) {
+ RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
+
+ RID base = light_storage->light_instance_get_base_light(p_light);
+ ERR_FAIL_COND(light_storage->light_get_type(base) != RS::LIGHT_DIRECTIONAL);
+
+ // Make sure our buffers and arrays exist.
+ _create_frustum_arrays();
+
+ // Setup a points buffer for our view frustum.
+ PackedByteArray points;
+ points.resize(8 * sizeof(float) * 3);
+
+ // Get info about our splits.
+ RS::LightDirectionalShadowMode shadow_mode = light_storage->light_directional_get_shadow_mode(base);
+ bool overlap = light_storage->light_directional_get_blend_splits(base);
+ int splits = 1;
+ if (shadow_mode == RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) {
+ splits = 4;
+ } else if (shadow_mode == RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) {
+ splits = 2;
+ }
+
+ // Setup our camera info (this is mostly a duplicate of the logic found in RendererSceneCull::_light_instance_setup_directional_shadow).
+ bool is_orthogonal = p_cam_projection.is_orthogonal();
+ real_t aspect = p_cam_projection.get_aspect();
+ real_t fov = 0.0;
+ Vector2 vp_he;
+ if (is_orthogonal) {
+ vp_he = p_cam_projection.get_viewport_half_extents();
+ } else {
+ fov = p_cam_projection.get_fov(); //this is actually yfov, because set aspect tries to keep it
+ }
+ real_t min_distance = p_cam_projection.get_z_near();
+ real_t max_distance = p_cam_projection.get_z_far();
+ real_t shadow_max = RSG::light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE);
+ if (shadow_max > 0 && !is_orthogonal) {
+ max_distance = MIN(shadow_max, max_distance);
+ }
+
+ // Make sure we've not got bad info coming in.
+ max_distance = MAX(max_distance, min_distance + 0.001);
+ min_distance = MIN(min_distance, max_distance);
+ real_t range = max_distance - min_distance;
+
+ real_t distances[5];
+ distances[0] = min_distance;
+ for (int i = 0; i < splits; i++) {
+ distances[i + 1] = min_distance + RSG::light_storage->light_get_param(base, RS::LightParam(RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET + i)) * range;
+ };
+ distances[splits] = max_distance;
+
+ Color colors[4] = {
+ Color(1.0, 0.0, 0.0, 0.1),
+ Color(0.0, 1.0, 0.0, 0.1),
+ Color(0.0, 0.0, 1.0, 0.1),
+ Color(1.0, 1.0, 0.0, 0.1),
+ };
+
+ for (int split = 0; split < splits; split++) {
+ // Load frustum points into vertex buffer.
+ uint8_t *w = points.ptrw();
+ Vector3 *vw = (Vector3 *)w;
+
+ Projection projection;
+
+ if (is_orthogonal) {
+ projection.set_orthogonal(vp_he.y * 2.0, aspect, distances[(split == 0 || !overlap) ? split : split - 1], distances[split + 1], false);
+ } else {
+ projection.set_perspective(fov, aspect, distances[(split == 0 || !overlap) ? split : split - 1], distances[split + 1], true);
+ }
+
+ bool res = projection.get_endpoints(p_cam_transform, vw);
+ ERR_CONTINUE(!res);
+
+ RD::get_singleton()->buffer_update(frustum.vertex_buffer, 0, 8 * sizeof(float) * 3, w);
+
+ // Get our light projection info.
+ Projection light_projection = light_storage->light_instance_get_shadow_camera(p_light, split);
+ Transform3D light_transform = light_storage->light_instance_get_shadow_transform(p_light, split);
+ Rect2 atlas_rect_norm = light_storage->light_instance_get_directional_shadow_atlas_rect(p_light, split);
+
+ if (!is_orthogonal) {
+ light_transform.orthogonalize();
+ }
+
+ // Setup our push constant.
+ ShadowFrustumPushConstant push_constant;
+ MaterialStorage::store_camera(light_projection * Projection(light_transform.inverse()), push_constant.mvp);
+ push_constant.color[0] = colors[split].r;
+ push_constant.color[1] = colors[split].g;
+ push_constant.color[2] = colors[split].b;
+ push_constant.color[3] = colors[split].a;
+
+ // Adjust our rect to our atlas position.
+ Rect2 rect = p_rect;
+ rect.position.x += atlas_rect_norm.position.x * rect.size.x;
+ rect.position.y += atlas_rect_norm.position.y * rect.size.y;
+ rect.size.x *= atlas_rect_norm.size.x;
+ rect.size.y *= atlas_rect_norm.size.y;
+
+ // And draw our frustum.
+ RD::FramebufferFormatID fb_format_id = RD::get_singleton()->framebuffer_get_format(p_dest_fb);
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, rect);
+
+ RID pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline);
+ RD::get_singleton()->draw_list_bind_vertex_array(draw_list, frustum.vertex_array);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, frustum.index_array);
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowFrustumPushConstant));
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+
+ pipeline = shadow_frustum.pipelines[SFP_WIREFRAME].get_render_pipeline(frustum.vertex_format, fb_format_id);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline);
+ RD::get_singleton()->draw_list_bind_vertex_array(draw_list, frustum.vertex_array);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, frustum.lines_array);
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowFrustumPushConstant));
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+
+ RD::get_singleton()->draw_list_end();
+
+ if (split < (splits - 1) && splits > 1) {
+ // Also draw it in the last split so we get a proper overview of the whole view frustum...
+
+ // Get our light projection info.
+ light_projection = light_storage->light_instance_get_shadow_camera(p_light, (splits - 1));
+ light_transform = light_storage->light_instance_get_shadow_transform(p_light, (splits - 1));
+ atlas_rect_norm = light_storage->light_instance_get_directional_shadow_atlas_rect(p_light, (splits - 1));
+
+ if (!is_orthogonal) {
+ light_transform.orthogonalize();
+ }
+
+ // Update our push constant.
+ MaterialStorage::store_camera(light_projection * Projection(light_transform.inverse()), push_constant.mvp);
+ push_constant.color[0] = colors[split].r;
+ push_constant.color[1] = colors[split].g;
+ push_constant.color[2] = colors[split].b;
+ push_constant.color[3] = colors[split].a;
+
+ // Adjust our rect to our atlas position.
+ rect = p_rect;
+ rect.position.x += atlas_rect_norm.position.x * rect.size.x;
+ rect.position.y += atlas_rect_norm.position.y * rect.size.y;
+ rect.size.x *= atlas_rect_norm.size.x;
+ rect.size.y *= atlas_rect_norm.size.y;
+
+ draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, rect);
+
+ pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline);
+ RD::get_singleton()->draw_list_bind_vertex_array(draw_list, frustum.vertex_array);
+ RD::get_singleton()->draw_list_bind_index_array(draw_list, frustum.index_array);
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowFrustumPushConstant));
+ RD::get_singleton()->draw_list_draw(draw_list, true);
+
+ RD::get_singleton()->draw_list_end();
+ }
+ }
+}
diff --git a/servers/rendering/renderer_rd/effects/debug_effects.h b/servers/rendering/renderer_rd/effects/debug_effects.h
new file mode 100644
index 0000000000..21b7b03f84
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/debug_effects.h
@@ -0,0 +1,85 @@
+/**************************************************************************/
+/* debug_effects.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 DEBUG_EFFECTS_RD_H
+#define DEBUG_EFFECTS_RD_H
+
+#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/effects/shadow_frustum.glsl.gen.h"
+#include "servers/rendering/renderer_scene_render.h"
+
+#include "servers/rendering_server.h"
+
+namespace RendererRD {
+
+class DebugEffects {
+private:
+ struct {
+ RD::VertexFormatID vertex_format;
+ RID vertex_buffer;
+ RID vertex_array;
+
+ RID index_buffer;
+ RID index_array;
+
+ RID lines_buffer;
+ RID lines_array;
+ } frustum;
+
+ struct ShadowFrustumPushConstant {
+ float mvp[16];
+ float color[4];
+ };
+
+ enum ShadowFrustumPipelines {
+ SFP_TRANSPARENT,
+ SFP_WIREFRAME,
+ SFP_MAX
+ };
+
+ struct {
+ ShadowFrustumShaderRD shader;
+ RID shader_version;
+ PipelineCacheRD pipelines[SFP_MAX];
+ } shadow_frustum;
+
+ void _create_frustum_arrays();
+
+protected:
+public:
+ DebugEffects();
+ ~DebugEffects();
+
+ void draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect);
+};
+
+} // namespace RendererRD
+
+#endif // DEBUG_EFFECTS_RD_H
diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp
index 96e3683560..b9346c0201 100644
--- a/servers/rendering/renderer_rd/effects/ss_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp
@@ -77,10 +77,9 @@ SSEffects::SSEffects() {
for (int pass = 0; pass < 4; pass++) {
for (int subPass = 0; subPass < sub_pass_count; subPass++) {
int a = pass;
- int b = subPass;
int spmap[5]{ 0, 1, 4, 3, 2 };
- b = spmap[subPass];
+ int b = spmap[subPass];
float ca, sa;
float angle0 = (float(a) + float(b) / float(sub_pass_count)) * Math_PI * 0.5f;
diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp
index 98ec3d6cc3..98d826b1f9 100644
--- a/servers/rendering/renderer_rd/environment/fog.cpp
+++ b/servers/rendering/renderer_rd/environment/fog.cpp
@@ -190,10 +190,11 @@ void Fog::init_fog_shader(uint32_t p_max_directional_lights, int p_roughness_lay
MaterialStorage *material_storage = MaterialStorage::get_singleton();
{
+ String defines = "#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n";
// Initialize local fog shader
Vector<String> volumetric_fog_modes;
volumetric_fog_modes.push_back("");
- volumetric_fog.shader.initialize(volumetric_fog_modes);
+ volumetric_fog.shader.initialize(volumetric_fog_modes, defines);
material_storage->shader_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_FOG, _create_fog_shader_funcs);
material_storage->material_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_FOG, _create_fog_material_funcs);
@@ -221,7 +222,6 @@ void Fog::init_fog_shader(uint32_t p_max_directional_lights, int p_roughness_lay
actions.usage_defines["ALBEDO"] = "#define ALBEDO_USED\n";
actions.usage_defines["EMISSION"] = "#define EMISSION_USED\n";
- actions.sampler_array_name = "material_samplers";
actions.base_texture_binding_index = 1;
actions.texture_layout_set = VolumetricFogShader::FogSet::FOG_SET_MATERIAL;
actions.base_uniform_string = "material.";
@@ -257,27 +257,6 @@ ALBEDO = vec3(1.0);
Vector<RD::Uniform> uniforms;
{
- Vector<RID> ids;
- ids.resize(12);
- RID *ids_ptr = ids.ptrw();
- ids_ptr[0] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[1] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[2] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[3] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[4] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[5] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[6] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[7] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[8] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[9] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[10] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[11] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
-
- RD::Uniform u(RD::UNIFORM_TYPE_SAMPLER, 1, ids);
- uniforms.push_back(u);
- }
-
- {
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 2;
@@ -285,8 +264,11 @@ ALBEDO = vec3(1.0);
uniforms.push_back(u);
}
+ uniforms.append_array(material_storage->get_default_sampler_uniforms(SAMPLERS_BINDING_FIRST_INDEX));
+
volumetric_fog.base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.default_shader_rd, VolumetricFogShader::FogSet::FOG_SET_BASE);
}
+
{
String defines = "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(p_max_directional_lights) + "\n";
defines += "\n#define MAX_SKY_LOD " + itos(p_roughness_layers - 1) + ".0\n";
diff --git a/servers/rendering/renderer_rd/environment/fog.h b/servers/rendering/renderer_rd/environment/fog.h
index 8a7c5c0082..206d7852a7 100644
--- a/servers/rendering/renderer_rd/environment/fog.h
+++ b/servers/rendering/renderer_rd/environment/fog.h
@@ -70,6 +70,8 @@ private:
mutable RID_Owner<FogVolumeInstance> fog_volume_instance_owner;
+ const int SAMPLERS_BINDING_FIRST_INDEX = 3;
+
/* Volumetric Fog */
struct VolumetricFogShader {
enum FogSet {
diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp
index d7eb0287d8..ebf3c5f619 100644
--- a/servers/rendering/renderer_rd/environment/sky.cpp
+++ b/servers/rendering/renderer_rd/environment/sky.cpp
@@ -236,16 +236,16 @@ void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineC
// Update uniform sets.
{
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.uniform_set, 0);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.uniform_set, SKY_SET_UNIFORMS);
if (p_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(p_uniform_set)) { // Material may not have a uniform set.
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, SKY_SET_MATERIAL);
}
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, SKY_SET_TEXTURES);
// Fog uniform set can be invalidated before drawing, so validate at draw time
if (sky_scene_state.fog_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sky_scene_state.fog_uniform_set)) {
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.fog_uniform_set, 3);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.fog_uniform_set, SKY_SET_FOG);
} else {
- RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.default_fog_uniform_set, 3);
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.default_fog_uniform_set, SKY_SET_FOG);
}
}
@@ -738,6 +738,7 @@ void SkyRD::init() {
sky_scene_state.directional_light_buffer = RD::get_singleton()->uniform_buffer_create(directional_light_buffer_size);
String defines = "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(sky_scene_state.max_directional_lights) + "\n";
+ defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n";
// Initialize sky
Vector<String> sky_modes;
@@ -806,13 +807,12 @@ void SkyRD::init() {
actions.renames["AT_CUBEMAP_PASS"] = "AT_CUBEMAP_PASS";
actions.renames["AT_HALF_RES_PASS"] = "AT_HALF_RES_PASS";
actions.renames["AT_QUARTER_RES_PASS"] = "AT_QUARTER_RES_PASS";
- actions.custom_samplers["RADIANCE"] = "material_samplers[3]";
+ actions.custom_samplers["RADIANCE"] = "SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP";
actions.usage_defines["HALF_RES_COLOR"] = "\n#define USES_HALF_RES_COLOR\n";
actions.usage_defines["QUARTER_RES_COLOR"] = "\n#define USES_QUARTER_RES_COLOR\n";
actions.render_mode_defines["disable_fog"] = "#define DISABLE_FOG\n";
actions.render_mode_defines["use_debanding"] = "#define USE_DEBANDING\n";
- actions.sampler_array_name = "material_samplers";
actions.base_texture_binding_index = 1;
actions.texture_layout_set = 1;
actions.base_uniform_string = "material.";
@@ -853,28 +853,6 @@ void sky() {
Vector<RD::Uniform> uniforms;
{
- Vector<RID> ids;
- ids.resize(12);
- RID *ids_ptr = ids.ptrw();
- ids_ptr[0] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[1] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[2] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[3] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[4] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[5] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[6] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[7] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[8] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[9] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[10] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[11] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
-
- RD::Uniform u(RD::UNIFORM_TYPE_SAMPLER, 0, ids);
-
- uniforms.push_back(u);
- }
-
- {
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 1;
@@ -898,6 +876,8 @@ void sky() {
uniforms.push_back(u);
}
+ uniforms.append_array(material_storage->get_default_sampler_uniforms(SAMPLERS_BINDING_FIRST_INDEX));
+
sky_scene_state.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_UNIFORMS);
}
@@ -1041,25 +1021,27 @@ void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, con
material = nullptr;
}
}
+ }
- if (!material) {
- sky_material = sky_shader.default_material;
- material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::MaterialStorage::SHADER_TYPE_SKY));
- }
+ if (!material) {
+ sky_material = sky_shader.default_material;
+ material = static_cast<SkyMaterialData *>(material_storage->material_get_data(sky_material, RendererRD::MaterialStorage::SHADER_TYPE_SKY));
+ }
- ERR_FAIL_COND(!material);
+ ERR_FAIL_COND(!material);
- shader_data = material->shader_data;
+ shader_data = material->shader_data;
- ERR_FAIL_COND(!shader_data);
+ ERR_FAIL_COND(!shader_data);
- material->set_as_used();
+ material->set_as_used();
- // Save our screen size, our buffers will already have been cleared
+ if (sky) {
+ // Save our screen size; our buffers will already have been cleared.
sky->screen_size.x = p_screen_size.x < 4 ? 4 : p_screen_size.x;
sky->screen_size.y = p_screen_size.y < 4 ? 4 : p_screen_size.y;
- // Trigger updating radiance buffers
+ // Trigger updating radiance buffers.
if (sky->radiance.is_null()) {
invalidate_sky(sky);
update_dirty_skys();
@@ -1085,107 +1067,109 @@ void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, con
sky->prev_position = p_cam_transform.origin;
sky->reflection.dirty = true;
}
+ }
+
+ sky_scene_state.ubo.directional_light_count = 0;
+ if (shader_data->uses_light) {
+ // Run through the list of lights in the scene and pick out the Directional Lights.
+ // This can't be done in RenderSceneRenderRD::_setup lights because that needs to be called
+ // after the depth prepass, but this runs before the depth prepass.
+ for (int i = 0; i < (int)p_lights.size(); i++) {
+ if (!light_storage->owns_light_instance(p_lights[i])) {
+ continue;
+ }
+ RID base = light_storage->light_instance_get_base_light(p_lights[i]);
+
+ ERR_CONTINUE(base.is_null());
+
+ RS::LightType type = light_storage->light_get_type(base);
+ if (type == RS::LIGHT_DIRECTIONAL && light_storage->light_directional_get_sky_mode(base) != RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_ONLY) {
+ SkyDirectionalLightData &sky_light_data = sky_scene_state.directional_lights[sky_scene_state.ubo.directional_light_count];
+ Transform3D light_transform = light_storage->light_instance_get_base_transform(p_lights[i]);
+ Vector3 world_direction = light_transform.basis.xform(Vector3(0, 0, 1)).normalized();
+
+ sky_light_data.direction[0] = world_direction.x;
+ sky_light_data.direction[1] = world_direction.y;
+ sky_light_data.direction[2] = world_direction.z;
+
+ float sign = light_storage->light_is_negative(base) ? -1 : 1;
+ sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY);
- sky_scene_state.ubo.directional_light_count = 0;
- if (shader_data->uses_light) {
- // Run through the list of lights in the scene and pick out the Directional Lights.
- // This can't be done in RenderSceneRenderRD::_setup lights because that needs to be called
- // after the depth prepass, but this runs before the depth prepass
- for (int i = 0; i < (int)p_lights.size(); i++) {
- if (!light_storage->owns_light_instance(p_lights[i])) {
- continue;
+ if (p_scene_render->is_using_physical_light_units()) {
+ sky_light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY);
}
- RID base = light_storage->light_instance_get_base_light(p_lights[i]);
-
- ERR_CONTINUE(base.is_null());
-
- RS::LightType type = light_storage->light_get_type(base);
- if (type == RS::LIGHT_DIRECTIONAL && light_storage->light_directional_get_sky_mode(base) != RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_ONLY) {
- SkyDirectionalLightData &sky_light_data = sky_scene_state.directional_lights[sky_scene_state.ubo.directional_light_count];
- Transform3D light_transform = light_storage->light_instance_get_base_transform(p_lights[i]);
- Vector3 world_direction = light_transform.basis.xform(Vector3(0, 0, 1)).normalized();
-
- sky_light_data.direction[0] = world_direction.x;
- sky_light_data.direction[1] = world_direction.y;
- sky_light_data.direction[2] = world_direction.z;
-
- float sign = light_storage->light_is_negative(base) ? -1 : 1;
- sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY);
-
- if (p_scene_render->is_using_physical_light_units()) {
- sky_light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY);
- }
-
- if (p_camera_attributes.is_valid()) {
- sky_light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_camera_attributes);
- }
-
- Color linear_col = light_storage->light_get_color(base).srgb_to_linear();
- sky_light_data.color[0] = linear_col.r;
- sky_light_data.color[1] = linear_col.g;
- sky_light_data.color[2] = linear_col.b;
-
- sky_light_data.enabled = true;
-
- float angular_diameter = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE);
- if (angular_diameter > 0.0) {
- // I know tan(0) is 0, but let's not risk it with numerical precision.
- // technically this will keep expanding until reaching the sun, but all we care
- // is expand until we reach the radius of the near plane (there can't be more occluders than that)
- angular_diameter = Math::tan(Math::deg_to_rad(angular_diameter));
- } else {
- angular_diameter = 0.0;
- }
- sky_light_data.size = angular_diameter;
- sky_scene_state.ubo.directional_light_count++;
- if (sky_scene_state.ubo.directional_light_count >= sky_scene_state.max_directional_lights) {
- break;
- }
+
+ if (p_camera_attributes.is_valid()) {
+ sky_light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_camera_attributes);
}
- }
- // Check whether the directional_light_buffer changes
- bool light_data_dirty = false;
-
- // Light buffer is dirty if we have fewer or more lights
- // If we have fewer lights, make sure that old lights are disabled
- if (sky_scene_state.ubo.directional_light_count != sky_scene_state.last_frame_directional_light_count) {
- light_data_dirty = true;
- for (uint32_t i = sky_scene_state.ubo.directional_light_count; i < sky_scene_state.max_directional_lights; i++) {
- sky_scene_state.directional_lights[i].enabled = false;
- sky_scene_state.last_frame_directional_lights[i].enabled = false;
+
+ Color linear_col = light_storage->light_get_color(base).srgb_to_linear();
+ sky_light_data.color[0] = linear_col.r;
+ sky_light_data.color[1] = linear_col.g;
+ sky_light_data.color[2] = linear_col.b;
+
+ sky_light_data.enabled = true;
+
+ float angular_diameter = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE);
+ if (angular_diameter > 0.0) {
+ // I know tan(0) is 0, but let's not risk it with numerical precision.
+ // Technically this will keep expanding until reaching the sun, but all we care about
+ // is expanding until we reach the radius of the near plane. There can't be more occluders than that.
+ angular_diameter = Math::tan(Math::deg_to_rad(angular_diameter));
+ } else {
+ angular_diameter = 0.0;
+ }
+ sky_light_data.size = angular_diameter;
+ sky_scene_state.ubo.directional_light_count++;
+ if (sky_scene_state.ubo.directional_light_count >= sky_scene_state.max_directional_lights) {
+ break;
}
}
+ }
+ // Check whether the directional_light_buffer changes.
+ bool light_data_dirty = false;
+
+ // Light buffer is dirty if we have fewer or more lights.
+ // If we have fewer lights, make sure that old lights are disabled.
+ if (sky_scene_state.ubo.directional_light_count != sky_scene_state.last_frame_directional_light_count) {
+ light_data_dirty = true;
+ for (uint32_t i = sky_scene_state.ubo.directional_light_count; i < sky_scene_state.max_directional_lights; i++) {
+ sky_scene_state.directional_lights[i].enabled = false;
+ sky_scene_state.last_frame_directional_lights[i].enabled = false;
+ }
+ }
- if (!light_data_dirty) {
- for (uint32_t i = 0; i < sky_scene_state.ubo.directional_light_count; i++) {
- if (sky_scene_state.directional_lights[i].direction[0] != sky_scene_state.last_frame_directional_lights[i].direction[0] ||
- sky_scene_state.directional_lights[i].direction[1] != sky_scene_state.last_frame_directional_lights[i].direction[1] ||
- sky_scene_state.directional_lights[i].direction[2] != sky_scene_state.last_frame_directional_lights[i].direction[2] ||
- sky_scene_state.directional_lights[i].energy != sky_scene_state.last_frame_directional_lights[i].energy ||
- sky_scene_state.directional_lights[i].color[0] != sky_scene_state.last_frame_directional_lights[i].color[0] ||
- sky_scene_state.directional_lights[i].color[1] != sky_scene_state.last_frame_directional_lights[i].color[1] ||
- sky_scene_state.directional_lights[i].color[2] != sky_scene_state.last_frame_directional_lights[i].color[2] ||
- sky_scene_state.directional_lights[i].enabled != sky_scene_state.last_frame_directional_lights[i].enabled ||
- sky_scene_state.directional_lights[i].size != sky_scene_state.last_frame_directional_lights[i].size) {
- light_data_dirty = true;
- break;
- }
+ if (!light_data_dirty) {
+ for (uint32_t i = 0; i < sky_scene_state.ubo.directional_light_count; i++) {
+ if (sky_scene_state.directional_lights[i].direction[0] != sky_scene_state.last_frame_directional_lights[i].direction[0] ||
+ sky_scene_state.directional_lights[i].direction[1] != sky_scene_state.last_frame_directional_lights[i].direction[1] ||
+ sky_scene_state.directional_lights[i].direction[2] != sky_scene_state.last_frame_directional_lights[i].direction[2] ||
+ sky_scene_state.directional_lights[i].energy != sky_scene_state.last_frame_directional_lights[i].energy ||
+ sky_scene_state.directional_lights[i].color[0] != sky_scene_state.last_frame_directional_lights[i].color[0] ||
+ sky_scene_state.directional_lights[i].color[1] != sky_scene_state.last_frame_directional_lights[i].color[1] ||
+ sky_scene_state.directional_lights[i].color[2] != sky_scene_state.last_frame_directional_lights[i].color[2] ||
+ sky_scene_state.directional_lights[i].enabled != sky_scene_state.last_frame_directional_lights[i].enabled ||
+ sky_scene_state.directional_lights[i].size != sky_scene_state.last_frame_directional_lights[i].size) {
+ light_data_dirty = true;
+ break;
}
}
+ }
- if (light_data_dirty) {
- RD::get_singleton()->buffer_update(sky_scene_state.directional_light_buffer, 0, sizeof(SkyDirectionalLightData) * sky_scene_state.max_directional_lights, sky_scene_state.directional_lights);
+ if (light_data_dirty) {
+ RD::get_singleton()->buffer_update(sky_scene_state.directional_light_buffer, 0, sizeof(SkyDirectionalLightData) * sky_scene_state.max_directional_lights, sky_scene_state.directional_lights);
- SkyDirectionalLightData *temp = sky_scene_state.last_frame_directional_lights;
- sky_scene_state.last_frame_directional_lights = sky_scene_state.directional_lights;
- sky_scene_state.directional_lights = temp;
- sky_scene_state.last_frame_directional_light_count = sky_scene_state.ubo.directional_light_count;
+ SkyDirectionalLightData *temp = sky_scene_state.last_frame_directional_lights;
+ sky_scene_state.last_frame_directional_lights = sky_scene_state.directional_lights;
+ sky_scene_state.directional_lights = temp;
+ sky_scene_state.last_frame_directional_light_count = sky_scene_state.ubo.directional_light_count;
+ if (sky) {
sky->reflection.dirty = true;
}
}
}
- //setup fog variables
+ // Setup fog variables.
sky_scene_state.ubo.volumetric_fog_enabled = false;
if (p_render_buffers.is_valid()) {
if (p_render_buffers->has_custom_data(RB_SCOPE_FOG)) {
@@ -1199,7 +1183,7 @@ void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, con
sky_scene_state.ubo.volumetric_fog_inv_length = 1.0;
}
- float fog_detail_spread = fog->spread; //reverse lookup
+ float fog_detail_spread = fog->spread; // Reverse lookup.
if (fog_detail_spread > 0.0) {
sky_scene_state.ubo.volumetric_fog_detail_spread = 1.0 / fog_detail_spread;
} else {
@@ -1212,9 +1196,9 @@ void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, con
sky_scene_state.view_count = p_view_count;
sky_scene_state.cam_transform = p_cam_transform;
- sky_scene_state.cam_projection = p_cam_projection; // We only use this when rendering a single view
+ sky_scene_state.cam_projection = p_cam_projection; // We only use this when rendering a single view.
- // Our info in our UBO is only used if we're rendering stereo
+ // Our info in our UBO is only used if we're rendering stereo.
for (uint32_t i = 0; i < p_view_count; i++) {
Projection view_inv_projection = p_view_projections[i].inverse();
if (p_view_count > 1) {
@@ -1231,7 +1215,7 @@ void SkyRD::setup_sky(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, con
sky_scene_state.ubo.view_eye_offsets[i][3] = 0.0;
}
- sky_scene_state.ubo.z_far = p_view_projections[0].get_z_far(); // Should be the same for all projection
+ sky_scene_state.ubo.z_far = p_view_projections[0].get_z_far(); // Should be the same for all projection.
sky_scene_state.ubo.fog_enabled = RendererSceneRenderRD::get_singleton()->environment_get_fog_enabled(p_env);
sky_scene_state.ubo.fog_density = RendererSceneRenderRD::get_singleton()->environment_get_fog_density(p_env);
sky_scene_state.ubo.fog_aerial_perspective = RendererSceneRenderRD::get_singleton()->environment_get_fog_aerial_perspective(p_env);
diff --git a/servers/rendering/renderer_rd/environment/sky.h b/servers/rendering/renderer_rd/environment/sky.h
index afc6274886..7aee65fd67 100644
--- a/servers/rendering/renderer_rd/environment/sky.h
+++ b/servers/rendering/renderer_rd/environment/sky.h
@@ -53,9 +53,10 @@ public:
SKY_SET_MATERIAL,
SKY_SET_TEXTURES,
SKY_SET_FOG,
- SKY_SET_MAX
};
+ const int SAMPLERS_BINDING_FIRST_INDEX = 4;
+
// Skys need less info from Directional Lights than the normal shaders
struct SkyDirectionalLightData {
float direction[3];
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index ce0b4a9f9c..7bca1dc3be 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -59,7 +59,7 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_specular()
if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
- render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR_MSAA, format, usage_bits, texture_samples);
+ render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR_MSAA, format, usage_bits, render_buffers->get_texture_samples());
}
}
}
@@ -81,7 +81,7 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_normal_rou
if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
- render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_ROUGHNESS_MSAA, format, usage_bits, texture_samples);
+ render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_ROUGHNESS_MSAA, format, usage_bits, render_buffers->get_texture_samples());
}
}
}
@@ -100,7 +100,7 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_voxelgi()
if (render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
- render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI_MSAA, format, usage_bits, texture_samples);
+ render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_VOXEL_GI_MSAA, format, usage_bits, render_buffers->get_texture_samples());
}
}
}
@@ -134,29 +134,6 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RenderS
render_buffers = p_render_buffers;
ERR_FAIL_NULL(render_buffers);
- RS::ViewportMSAA msaa_3d = render_buffers->get_msaa_3d();
-
- if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) {
- RD::DataFormat format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
- uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
-
- const RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = {
- RD::TEXTURE_SAMPLES_1,
- RD::TEXTURE_SAMPLES_2,
- RD::TEXTURE_SAMPLES_4,
- RD::TEXTURE_SAMPLES_8,
- };
-
- texture_samples = ts[msaa_3d];
-
- p_render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_COLOR_MSAA, format, usage_bits, texture_samples);
-
- format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT;
- usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
-
- p_render_buffers->create_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA, format, usage_bits, texture_samples);
- }
-
if (cluster_builder == nullptr) {
cluster_builder = memnew(ClusterBuilderRD);
}
@@ -171,8 +148,8 @@ RID RenderForwardClustered::RenderBufferDataForwardClustered::get_color_only_fb(
bool use_msaa = render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED;
- RID color = use_msaa ? render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_COLOR_MSAA) : render_buffers->get_internal_texture();
- RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture();
+ RID color = use_msaa ? render_buffers->get_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA) : render_buffers->get_internal_texture();
+ RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture();
if (render_buffers->has_texture(RB_SCOPE_VRS, RB_TEXTURE)) {
RID vrs_texture = render_buffers->get_texture(RB_SCOPE_VRS, RB_TEXTURE);
@@ -188,7 +165,7 @@ RID RenderForwardClustered::RenderBufferDataForwardClustered::get_color_pass_fb(
bool use_msaa = render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED;
int v_count = (p_color_pass_flags & COLOR_PASS_FLAG_MULTIVIEW) ? render_buffers->get_view_count() : 1;
- RID color = use_msaa ? render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_COLOR_MSAA) : render_buffers->get_internal_texture();
+ RID color = use_msaa ? render_buffers->get_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA) : render_buffers->get_internal_texture();
RID specular;
if (p_color_pass_flags & COLOR_PASS_FLAG_SEPARATE_SPECULAR) {
@@ -202,7 +179,7 @@ RID RenderForwardClustered::RenderBufferDataForwardClustered::get_color_pass_fb(
velocity_buffer = render_buffers->get_velocity_buffer(use_msaa);
}
- RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture();
+ RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture();
if (render_buffers->has_texture(RB_SCOPE_VRS, RB_TEXTURE)) {
RID vrs_texture = render_buffers->get_texture(RB_SCOPE_VRS, RB_TEXTURE);
@@ -217,7 +194,7 @@ RID RenderForwardClustered::RenderBufferDataForwardClustered::get_depth_fb(Depth
ERR_FAIL_NULL_V(render_buffers, RID());
bool use_msaa = render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED;
- RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture();
+ RID depth = use_msaa ? render_buffers->get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA) : render_buffers->get_depth_texture();
switch (p_type) {
case DEPTH_FB: {
@@ -802,6 +779,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
scene_state.used_screen_texture = false;
scene_state.used_normal_texture = false;
scene_state.used_depth_texture = false;
+ scene_state.used_lightmap = false;
}
uint32_t lightmap_captures_used = 0;
@@ -944,25 +922,27 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
// LOD
if (p_render_data->scene_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) {
- // Get the LOD support points on the mesh AABB.
- Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z));
- Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z));
-
- // Get the distances to those points on the AABB from the camera origin.
- float distance_min = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_min);
- float distance_max = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_max);
-
float distance = 0.0;
- if (distance_min * distance_max < 0.0) {
- //crossing plane
- distance = 0.0;
- } else if (distance_min >= 0.0) {
- distance = distance_min;
- } else if (distance_max <= 0.0) {
- distance = -distance_max;
+ // Check if camera is NOT inside the mesh AABB.
+ if (!inst->transformed_aabb.has_point(p_render_data->scene_data->cam_transform.origin)) {
+ // Get the LOD support points on the mesh AABB.
+ Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z));
+ Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z));
+
+ // Get the distances to those points on the AABB from the camera origin.
+ float distance_min = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_min);
+ float distance_max = (float)p_render_data->scene_data->cam_transform.origin.distance_to(lod_support_max);
+
+ if (distance_min * distance_max < 0.0) {
+ //crossing plane
+ distance = 0.0;
+ } else if (distance_min >= 0.0) {
+ distance = distance_min;
+ } else if (distance_max <= 0.0) {
+ distance = -distance_max;
+ }
}
-
if (p_render_data->scene_data->cam_orthogonal) {
distance = 1.0;
}
@@ -1015,6 +995,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
if (uses_lightmap) {
surf->sort.uses_lightmap = 1;
+ scene_state.used_lightmap = true;
}
if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_SUBSURFACE_SCATTERING) {
@@ -1649,6 +1630,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
if (rb->get_use_taa() || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS) {
color_pass_flags |= COLOR_PASS_FLAG_MOTION_VECTORS;
+ scene_shader.enable_advanced_shader_group();
}
if (p_render_data->voxel_gi_instances->size() > 0) {
@@ -1668,6 +1650,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
if (p_render_data->scene_data->view_count > 1) {
color_pass_flags |= COLOR_PASS_FLAG_MULTIVIEW;
+ // Try enabling here in case is_xr_enabled() returns false.
+ scene_shader.shader.enable_group(SceneShaderForwardClustered::SHADER_GROUP_MULTIVIEW);
}
color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags);
@@ -1733,6 +1717,11 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
color_pass_flags |= COLOR_PASS_FLAG_SEPARATE_SPECULAR;
color_framebuffer = rb_data->get_color_pass_fb(color_pass_flags);
}
+
+ if (using_sss || using_separate_specular || scene_state.used_lightmap || using_voxelgi) {
+ scene_shader.enable_advanced_shader_group(p_render_data->scene_data->view_count > 1);
+ }
+
RID radiance_texture;
bool draw_sky = false;
bool draw_sky_fog_only = false;
@@ -1876,11 +1865,11 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
RD::get_singleton()->barrier(RD::BARRIER_MASK_RASTER, RD::BARRIER_MASK_COMPUTE);
}
for (uint32_t v = 0; v < rb->get_view_count(); v++) {
- resolve_effects->resolve_gi(rb_data->get_depth_msaa(v), rb_data->get_normal_roughness_msaa(v), using_voxelgi ? rb_data->get_voxelgi_msaa(v) : RID(), rb->get_depth_texture(v), rb_data->get_normal_roughness(v), using_voxelgi ? rb_data->get_voxelgi(v) : RID(), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]);
+ resolve_effects->resolve_gi(rb->get_depth_msaa(v), rb_data->get_normal_roughness_msaa(v), using_voxelgi ? rb_data->get_voxelgi_msaa(v) : RID(), rb->get_depth_texture(v), rb_data->get_normal_roughness(v), using_voxelgi ? rb_data->get_voxelgi(v) : RID(), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]);
}
} else if (finish_depth) {
for (uint32_t v = 0; v < rb->get_view_count(); v++) {
- resolve_effects->resolve_depth(rb_data->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]);
+ resolve_effects->resolve_depth(rb->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]);
}
}
RD::get_singleton()->draw_command_end_label();
@@ -1988,7 +1977,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
if (!can_continue_color) {
// Handle views individual, might want to look at rewriting our resolve to do both layers in one pass.
for (uint32_t v = 0; v < rb->get_view_count(); v++) {
- RD::get_singleton()->texture_resolve_multisample(rb_data->get_color_msaa(v), rb->get_internal_texture(v));
+ RD::get_singleton()->texture_resolve_multisample(rb->get_color_msaa(v), rb->get_internal_texture(v));
}
if (using_separate_specular) {
for (uint32_t v = 0; v < rb->get_view_count(); v++) {
@@ -1999,7 +1988,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
if (!can_continue_depth) {
for (uint32_t v = 0; v < rb->get_view_count(); v++) {
- resolve_effects->resolve_depth(rb_data->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]);
+ resolve_effects->resolve_depth(rb->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]);
}
}
}
@@ -2075,8 +2064,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
if (rb_data.is_valid() && rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
for (uint32_t v = 0; v < rb->get_view_count(); v++) {
- RD::get_singleton()->texture_resolve_multisample(rb_data->get_color_msaa(v), rb->get_internal_texture(v));
- resolve_effects->resolve_depth(rb_data->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]);
+ RD::get_singleton()->texture_resolve_multisample(rb->get_color_msaa(v), rb->get_internal_texture(v));
+ resolve_effects->resolve_depth(rb->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]);
}
if (taa && rb->get_use_taa()) {
taa->msaa_resolve(rb);
@@ -2106,7 +2095,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
}
if (rb_data.is_valid()) {
- _render_buffers_debug_draw(rb, p_render_data->shadow_atlas, p_render_data->occluder_debug_tex);
+ _render_buffers_debug_draw(p_render_data);
if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb->has_custom_data(RB_SCOPE_SDFGI)) {
Ref<RendererRD::GI::SDFGI> sdfgi = rb->get_custom_data(RB_SCOPE_SDFGI);
@@ -2124,34 +2113,36 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
}
}
-void RenderForwardClustered::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) {
+void RenderForwardClustered::_render_buffers_debug_draw(const RenderDataRD *p_render_data) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
- ERR_FAIL_COND(p_render_buffers.is_null());
- Ref<RenderBufferDataForwardClustered> rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
+ Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers;
+ ERR_FAIL_COND(rb.is_null());
+
+ Ref<RenderBufferDataForwardClustered> rb_data = rb->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED);
ERR_FAIL_COND(rb_data.is_null());
- RendererSceneRenderRD::_render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occlusion_buffer);
+ RendererSceneRenderRD::_render_buffers_debug_draw(p_render_data);
- RID render_target = p_render_buffers->get_render_target();
+ RID render_target = rb->get_render_target();
- if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SSAO && p_render_buffers->has_texture(RB_SCOPE_SSAO, RB_FINAL)) {
- RID final = p_render_buffers->get_texture_slice(RB_SCOPE_SSAO, RB_FINAL, 0, 0);
+ if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SSAO && rb->has_texture(RB_SCOPE_SSAO, RB_FINAL)) {
+ RID final = rb->get_texture_slice(RB_SCOPE_SSAO, RB_FINAL, 0, 0);
Size2i rtsize = texture_storage->render_target_get_size(render_target);
copy_effects->copy_to_fb_rect(final, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, true);
}
- if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SSIL && p_render_buffers->has_texture(RB_SCOPE_SSIL, RB_FINAL)) {
- RID final = p_render_buffers->get_texture_slice(RB_SCOPE_SSIL, RB_FINAL, 0, 0);
+ if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SSIL && rb->has_texture(RB_SCOPE_SSIL, RB_FINAL)) {
+ RID final = rb->get_texture_slice(RB_SCOPE_SSIL, RB_FINAL, 0, 0);
Size2i rtsize = texture_storage->render_target_get_size(render_target);
copy_effects->copy_to_fb_rect(final, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false);
}
- if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && p_render_buffers->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT)) {
+ if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && rb->has_texture(RB_SCOPE_GI, RB_TEX_AMBIENT)) {
Size2i rtsize = texture_storage->render_target_get_size(render_target);
- RID ambient_texture = p_render_buffers->get_texture(RB_SCOPE_GI, RB_TEX_AMBIENT);
- RID reflection_texture = p_render_buffers->get_texture(RB_SCOPE_GI, RB_TEX_REFLECTION);
- copy_effects->copy_to_fb_rect(ambient_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture, p_render_buffers->get_view_count() > 1);
+ RID ambient_texture = rb->get_texture(RB_SCOPE_GI, RB_TEX_AMBIENT);
+ RID reflection_texture = rb->get_texture(RB_SCOPE_GI, RB_TEX_REFLECTION);
+ copy_effects->copy_to_fb_rect(ambient_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture, rb->get_view_count() > 1);
}
}
@@ -2503,6 +2494,8 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform
render_data.cluster_max_elements = 32;
render_data.instances = &p_instances;
+ scene_shader.enable_advanced_shader_group();
+
_update_render_base_uniform_set();
_setup_environment(&render_data, true, Vector2(1, 1), false, Color());
@@ -2552,6 +2545,8 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance
render_data.cluster_max_elements = 32;
render_data.instances = &p_instances;
+ scene_shader.enable_advanced_shader_group();
+
_update_render_base_uniform_set();
_setup_environment(&render_data, true, Vector2(1, 1), false, Color());
@@ -2717,28 +2712,6 @@ void RenderForwardClustered::_update_render_base_uniform_set() {
Vector<RD::Uniform> uniforms;
{
- Vector<RID> ids;
- ids.resize(12);
- RID *ids_ptr = ids.ptrw();
- ids_ptr[0] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[1] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[2] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[3] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[4] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[5] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[6] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[7] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[8] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[9] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[10] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[11] = material_storage->sampler_rd_get_custom(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
-
- RD::Uniform u(RD::UNIFORM_TYPE_SAMPLER, 1, ids);
-
- uniforms.push_back(u);
- }
-
- {
RD::Uniform u;
u.binding = 2;
u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
@@ -2889,6 +2862,8 @@ void RenderForwardClustered::_update_render_base_uniform_set() {
uniforms.push_back(u);
}
+ uniforms.append_array(material_storage->get_default_sampler_uniforms(SAMPLERS_BINDING_FIRST_INDEX));
+
render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_shader.default_shader_rd, SCENE_UNIFORM_SET);
}
}
@@ -3360,6 +3335,10 @@ void RenderForwardClustered::sdfgi_update(const Ref<RenderSceneBuffers> &p_rende
return;
}
+ // Ensure advanced shaders are available if SDFGI is used.
+ // Call here as this is the first entry point for SDFGI.
+ scene_shader.enable_advanced_shader_group();
+
static const uint32_t history_frames_to_converge[RS::ENV_SDFGI_CONVERGE_MAX] = { 5, 10, 15, 20, 25, 30 };
uint32_t requested_history_size = history_frames_to_converge[gi.sdfgi_frames_to_converge];
@@ -3973,6 +3952,7 @@ RenderForwardClustered::RenderForwardClustered() {
}
{
defines += "\n#define MATERIAL_UNIFORM_SET " + itos(MATERIAL_UNIFORM_SET) + "\n";
+ defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n";
}
#ifdef REAL_T_IS_DOUBLE
{
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index 7ab9e67bac..a89c77c652 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -60,9 +60,11 @@ class RenderForwardClustered : public RendererSceneRenderRD {
SCENE_UNIFORM_SET = 0,
RENDER_PASS_UNIFORM_SET = 1,
TRANSFORMS_UNIFORM_SET = 2,
- MATERIAL_UNIFORM_SET = 3
+ MATERIAL_UNIFORM_SET = 3,
};
+ const int SAMPLERS_BINDING_FIRST_INDEX = 16;
+
enum {
SPEC_CONSTANT_SOFT_SHADOW_SAMPLES = 6,
SPEC_CONSTANT_PENUMBRA_SHADOW_SAMPLES = 7,
@@ -98,7 +100,6 @@ class RenderForwardClustered : public RendererSceneRenderRD {
private:
RenderSceneBuffersRD *render_buffers = nullptr;
- RD::TextureSamples texture_samples = RD::TEXTURE_SAMPLES_1;
public:
ClusterBuilderRD *cluster_builder = nullptr;
@@ -120,12 +121,6 @@ class RenderForwardClustered : public RendererSceneRenderRD {
RID render_sdfgi_uniform_set;
- RID get_color_msaa() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_COLOR_MSAA); }
- RID get_color_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_COLOR_MSAA, p_layer, 0); }
-
- RID get_depth_msaa() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA); }
- RID get_depth_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_DEPTH_MSAA, p_layer, 0); }
-
void ensure_specular();
bool has_specular() const { return render_buffers->has_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR); }
RID get_specular() const { return render_buffers->get_texture(RB_SCOPE_FORWARD_CLUSTERED, RB_TEX_SPECULAR); }
@@ -326,6 +321,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
bool used_normal_texture = false;
bool used_depth_texture = false;
bool used_sss = false;
+ bool used_lightmap = false;
struct ShadowPass {
uint32_t element_from;
@@ -618,7 +614,7 @@ protected:
/* Rendering */
virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) override;
- virtual void _render_buffers_debug_draw(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) override;
+ virtual void _render_buffers_debug_draw(const RenderDataRD *p_render_data) override;
virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override;
virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override;
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index fe4ec70f98..298a6c0752 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -301,7 +301,7 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
if (k == PIPELINE_VERSION_COLOR_PASS) {
for (int l = 0; l < PIPELINE_COLOR_PASS_FLAG_COUNT; l++) {
- if (!shader_singleton->valid_color_pass_pipelines.has(l)) {
+ if (!shader_singleton->valid_color_pass_pipelines[l]) {
continue;
}
@@ -476,16 +476,16 @@ void SceneShaderForwardClustered::init(const String p_defines) {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
{
- Vector<String> shader_versions;
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_DEPTH_PASS
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_DEPTH_PASS_DP
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n"); // SHADER_VERSION_DEPTH_PASS_WITH_SDF
- shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_DEPTH_PASS_MULTIVIEW
- shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW
- shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW
+ Vector<ShaderRD::VariantDefine> shader_versions;
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n", true)); // SHADER_VERSION_DEPTH_PASS
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n", true)); // SHADER_VERSION_DEPTH_PASS_DP
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_BASE, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", true)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_ADVANCED, "\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_SDF
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n", false)); // SHADER_VERSION_DEPTH_PASS_MULTIVIEW
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW
+ shader_versions.push_back(ShaderRD::VariantDefine(SHADER_GROUP_MULTIVIEW, "\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n", false)); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW
Vector<String> color_pass_flags = {
"\n#define MODE_SEPARATE_SPECULAR\n", // SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR
@@ -501,54 +501,38 @@ void SceneShaderForwardClustered::init(const String p_defines) {
version += color_pass_flags[j];
}
}
- shader_versions.push_back(version);
+
+ // Assign a group based on what features this pass contains.
+ ShaderGroup group = SHADER_GROUP_BASE;
+ bool advanced_group = (i & SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR) || (i & SHADER_COLOR_PASS_FLAG_LIGHTMAP) || (i & SHADER_COLOR_PASS_FLAG_MOTION_VECTORS);
+ bool multiview_group = i & SHADER_COLOR_PASS_FLAG_MULTIVIEW;
+ if (advanced_group && multiview_group) {
+ group = SHADER_GROUP_ADVANCED_MULTIVIEW;
+ } else if (advanced_group) {
+ group = SHADER_GROUP_ADVANCED;
+ } else if (multiview_group) {
+ group = SHADER_GROUP_MULTIVIEW;
+ }
+
+ shader_versions.push_back(ShaderRD::VariantDefine(group, version, false));
}
shader.initialize(shader_versions, p_defines);
- if (!RendererCompositorRD::get_singleton()->is_xr_enabled()) {
- shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_MULTIVIEW, false);
- shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW, false);
- shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW, false);
-
- // Disable Color Passes
- for (int i = 0; i < SHADER_COLOR_PASS_FLAG_COUNT; i++) {
- // Selectively disable any shader pass that includes Multiview.
- if ((i & SHADER_COLOR_PASS_FLAG_MULTIVIEW)) {
- shader.set_variant_enabled(i + SHADER_VERSION_COLOR_PASS, false);
- }
- }
+ if (RendererCompositorRD::get_singleton()->is_xr_enabled()) {
+ shader.enable_group(SHADER_GROUP_MULTIVIEW);
}
}
- valid_color_pass_pipelines.insert(0);
-
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_TRANSPARENT | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
-
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR | PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
-
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_LIGHTMAP | PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
-
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_MULTIVIEW);
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_MULTIVIEW | PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
-
- valid_color_pass_pipelines.insert(PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS);
+ // Set flag to true if a combination is valid.
+ // The only invalid combinations are those that include both TRANSPARENT and SEPARATE_SPECULAR.
+ for (int i = 0; i < PIPELINE_COLOR_PASS_FLAG_COUNT; i++) {
+ if ((i & PIPELINE_COLOR_PASS_FLAG_TRANSPARENT) && (i & PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR)) {
+ valid_color_pass_pipelines[i] = false;
+ } else {
+ valid_color_pass_pipelines[i] = true;
+ }
+ }
material_storage->shader_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_shader_funcs);
material_storage->material_set_data_request_function(RendererRD::MaterialStorage::SHADER_TYPE_3D, _create_material_funcs);
@@ -701,7 +685,7 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n";
actions.render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n";
actions.render_mode_defines["particle_trails"] = "#define USE_PARTICLE_TRAILS\n";
- actions.render_mode_defines["depth_draw_opaque"] = "#define USE_OPAQUE_PREPASS\n";
+ actions.render_mode_defines["depth_prepass_alpha"] = "#define USE_OPAQUE_PREPASS\n";
bool force_lambert = GLOBAL_GET("rendering/shading/overrides/force_lambert_over_burley");
@@ -724,7 +708,6 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n";
- actions.sampler_array_name = "material_samplers";
actions.base_texture_binding_index = 1;
actions.texture_layout_set = RenderForwardClustered::MATERIAL_UNIFORM_SET;
actions.base_uniform_string = "material.";
@@ -855,3 +838,11 @@ void SceneShaderForwardClustered::set_default_specialization_constants(const Vec
}
}
}
+
+void SceneShaderForwardClustered::enable_advanced_shader_group(bool p_needs_multiview) {
+ if (p_needs_multiview || RendererCompositorRD::get_singleton()->is_xr_enabled()) {
+ shader.enable_group(SHADER_GROUP_ADVANCED_MULTIVIEW);
+ } else {
+ shader.enable_group(SHADER_GROUP_ADVANCED);
+ }
+}
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
index bdafc4c27e..761b74defa 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
@@ -41,6 +41,13 @@ private:
static SceneShaderForwardClustered *singleton;
public:
+ enum ShaderGroup {
+ SHADER_GROUP_BASE, // Always compiled at the beginning.
+ SHADER_GROUP_ADVANCED,
+ SHADER_GROUP_MULTIVIEW,
+ SHADER_GROUP_ADVANCED_MULTIVIEW,
+ };
+
enum ShaderVersion {
SHADER_VERSION_DEPTH_PASS,
SHADER_VERSION_DEPTH_PASS_DP,
@@ -78,8 +85,8 @@ public:
};
enum PipelineColorPassFlags {
- PIPELINE_COLOR_PASS_FLAG_TRANSPARENT = 1 << 0,
- PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 1,
+ PIPELINE_COLOR_PASS_FLAG_TRANSPARENT = 1 << 0, // Can't combine with SEPARATE_SPECULAR.
+ PIPELINE_COLOR_PASS_FLAG_SEPARATE_SPECULAR = 1 << 1, // Can't combine with TRANSPARENT.
PIPELINE_COLOR_PASS_FLAG_LIGHTMAP = 1 << 2,
PIPELINE_COLOR_PASS_FLAG_MULTIVIEW = 1 << 3,
PIPELINE_COLOR_PASS_FLAG_MOTION_VECTORS = 1 << 4,
@@ -242,12 +249,13 @@ public:
ShaderData *debug_shadow_splits_material_shader_ptr = nullptr;
Vector<RD::PipelineSpecializationConstant> default_specialization_constants;
- HashSet<uint32_t> valid_color_pass_pipelines;
+ bool valid_color_pass_pipelines[PIPELINE_COLOR_PASS_FLAG_COUNT];
SceneShaderForwardClustered();
~SceneShaderForwardClustered();
void init(const String p_defines);
void set_default_specialization_constants(const Vector<RD::PipelineSpecializationConstant> &p_constants);
+ void enable_advanced_shader_group(bool p_needs_multiview = false);
};
} // namespace RendererSceneRenderImplementation
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 2c75b05595..2053c3a9a6 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -136,30 +136,6 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RenderSceneBu
render_buffers = p_render_buffers;
ERR_FAIL_NULL(render_buffers); // Huh? really?
-
- RS::ViewportMSAA msaa_3d = render_buffers->get_msaa_3d();
- if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) {
- // Create our MSAA textures...
-
- RD::DataFormat format = render_buffers->get_base_data_format();
- uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
-
- const RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = {
- RD::TEXTURE_SAMPLES_1,
- RD::TEXTURE_SAMPLES_2,
- RD::TEXTURE_SAMPLES_4,
- RD::TEXTURE_SAMPLES_8,
- };
-
- texture_samples = ts[msaa_3d];
-
- render_buffers->create_texture(RB_SCOPE_MOBILE, RB_TEX_COLOR_MSAA, format, usage_bits, texture_samples);
-
- usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
- format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, usage_bits) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT;
-
- render_buffers->create_texture(RB_SCOPE_MOBILE, RB_TEX_DEPTH_MSAA, format, usage_bits, texture_samples);
- }
}
RID RenderForwardMobile::RenderBufferDataForwardMobile::get_color_fbs(FramebufferConfigType p_config_type) {
@@ -185,8 +161,8 @@ RID RenderForwardMobile::RenderBufferDataForwardMobile::get_color_fbs(Framebuffe
Vector<RID> textures;
int color_buffer_id = 0;
- textures.push_back(use_msaa ? get_color_msaa() : render_buffers->get_internal_texture()); // 0 - color buffer
- textures.push_back(use_msaa ? get_depth_msaa() : render_buffers->get_depth_texture()); // 1 - depth buffer
+ textures.push_back(use_msaa ? render_buffers->get_color_msaa() : render_buffers->get_internal_texture()); // 0 - color buffer
+ textures.push_back(use_msaa ? render_buffers->get_depth_msaa() : render_buffers->get_depth_texture()); // 1 - depth buffer
if (vrs_texture.is_valid()) {
textures.push_back(vrs_texture); // 2 - vrs texture
}
@@ -805,24 +781,20 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
clear_color.r *= bg_energy_multiplier;
clear_color.g *= bg_energy_multiplier;
clear_color.b *= bg_energy_multiplier;
- /*
- if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment)) {
+ if (environment_get_fog_enabled(p_render_data->environment)) {
draw_sky_fog_only = true;
RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear()));
}
- */
} break;
case RS::ENV_BG_COLOR: {
clear_color = environment_get_bg_color(p_render_data->environment);
clear_color.r *= bg_energy_multiplier;
clear_color.g *= bg_energy_multiplier;
clear_color.b *= bg_energy_multiplier;
- /*
- if (p_render_data->render_buffers->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment)) {
+ if (environment_get_fog_enabled(p_render_data->environment)) {
draw_sky_fog_only = true;
RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear()));
}
- */
} break;
case RS::ENV_BG_SKY: {
draw_sky = true;
@@ -932,13 +904,19 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
{
// regular forward for now
Vector<Color> c;
- c.push_back(clear_color.srgb_to_linear()); // our render buffer
- if (rb_data.is_valid()) {
- if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
- c.push_back(clear_color.srgb_to_linear()); // our resolve buffer
+ {
+ Color cc = clear_color.srgb_to_linear();
+ if (rb_data.is_valid()) {
+ cc.a = 0; // For transparent viewport backgrounds.
}
- if (using_subpass_post_process) {
- c.push_back(Color()); // our 2D buffer we're copying into
+ c.push_back(cc); // Our render buffer.
+ if (rb_data.is_valid()) {
+ if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) {
+ c.push_back(clear_color.srgb_to_linear()); // Our resolve buffer.
+ }
+ if (using_subpass_post_process) {
+ c.push_back(Color()); // Our 2D buffer we're copying into.
+ }
}
}
@@ -1083,7 +1061,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
_disable_clear_request(p_render_data);
}
- _render_buffers_debug_draw(rb, p_render_data->shadow_atlas, p_render_data->occluder_debug_tex);
+ _render_buffers_debug_draw(p_render_data);
}
/* these are being called from RendererSceneRenderRD::_pre_opaque_render */
@@ -1245,14 +1223,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 +1341,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();
}
@@ -1559,28 +1538,6 @@ void RenderForwardMobile::_update_render_base_uniform_set() {
Vector<RD::Uniform> uniforms;
{
- Vector<RID> ids;
- ids.resize(12);
- RID *ids_ptr = ids.ptrw();
- ids_ptr[0] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[1] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[2] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[3] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[4] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[5] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[6] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[7] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[8] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[9] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[10] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[11] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
-
- RD::Uniform u(RD::UNIFORM_TYPE_SAMPLER, 1, ids);
-
- uniforms.push_back(u);
- }
-
- {
RD::Uniform u;
u.binding = 2;
u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
@@ -1723,6 +1680,8 @@ void RenderForwardMobile::_update_render_base_uniform_set() {
uniforms.push_back(u);
}
+ uniforms.append_array(material_storage->get_default_sampler_uniforms(SAMPLERS_BINDING_FIRST_INDEX));
+
render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_shader.default_shader_rd, SCENE_UNIFORM_SET);
}
}
@@ -2808,6 +2767,7 @@ RenderForwardMobile::RenderForwardMobile() {
}
{
defines += "\n#define MATERIAL_UNIFORM_SET " + itos(MATERIAL_UNIFORM_SET) + "\n";
+ defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n";
}
#ifdef REAL_T_IS_DOUBLE
{
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index 214b39c496..f2913dd185 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -56,9 +56,11 @@ private:
SCENE_UNIFORM_SET = 0,
RENDER_PASS_UNIFORM_SET = 1,
TRANSFORMS_UNIFORM_SET = 2,
- MATERIAL_UNIFORM_SET = 3
+ MATERIAL_UNIFORM_SET = 3,
};
+ const int SAMPLERS_BINDING_FIRST_INDEX = 15;
+
enum {
SPEC_CONSTANT_USING_PROJECTOR = 0,
@@ -118,19 +120,12 @@ private:
FB_CONFIG_MAX
};
- RID get_color_msaa() const { return render_buffers->get_texture(RB_SCOPE_MOBILE, RB_TEX_COLOR_MSAA); }
- RID get_color_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_MOBILE, RB_TEX_COLOR_MSAA, p_layer, 0); }
-
- RID get_depth_msaa() const { return render_buffers->get_texture(RB_SCOPE_MOBILE, RB_TEX_DEPTH_MSAA); }
- RID get_depth_msaa(uint32_t p_layer) { return render_buffers->get_texture_slice(RB_SCOPE_MOBILE, RB_TEX_DEPTH_MSAA, p_layer, 0); }
-
RID get_color_fbs(FramebufferConfigType p_config_type);
virtual void free_data() override;
virtual void configure(RenderSceneBuffersRD *p_render_buffers) override;
private:
RenderSceneBuffersRD *render_buffers = nullptr;
- RD::TextureSamples texture_samples = RD::TEXTURE_SAMPLES_1;
};
virtual void setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) override;
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index 6603b97963..e4498ac533 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -589,7 +589,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n";
actions.render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n";
actions.render_mode_defines["particle_trails"] = "#define USE_PARTICLE_TRAILS\n";
- actions.render_mode_defines["depth_draw_opaque"] = "#define USE_OPAQUE_PREPASS\n";
+ actions.render_mode_defines["depth_prepass_alpha"] = "#define USE_OPAQUE_PREPASS\n";
bool force_lambert = GLOBAL_GET("rendering/shading/overrides/force_lambert_over_burley");
if (!force_lambert) {
@@ -611,7 +611,6 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n";
- actions.sampler_array_name = "material_samplers";
actions.base_texture_binding_index = 1;
actions.texture_layout_set = RenderForwardMobile::MATERIAL_UNIFORM_SET;
actions.base_uniform_string = "material.";
diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.cpp b/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
index f3595c5917..c00e5f8b5e 100644
--- a/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
+++ b/servers/rendering/renderer_rd/pipeline_cache_rd.cpp
@@ -89,7 +89,7 @@ void PipelineCacheRD::setup(RID p_shader, RD::RenderPrimitive p_primitive, const
ERR_FAIL_COND(p_shader.is_null());
_clear();
shader = p_shader;
- input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(p_shader);
+ input_mask = 0;
render_primitive = p_primitive;
rasterization_state = p_rasterization_state;
multisample_state = p_multisample;
diff --git a/servers/rendering/renderer_rd/pipeline_cache_rd.h b/servers/rendering/renderer_rd/pipeline_cache_rd.h
index 58c1278312..52877109f7 100644
--- a/servers/rendering/renderer_rd/pipeline_cache_rd.h
+++ b/servers/rendering/renderer_rd/pipeline_cache_rd.h
@@ -91,7 +91,11 @@ public:
return result;
}
- _FORCE_INLINE_ uint32_t get_vertex_input_mask() const {
+ _FORCE_INLINE_ uint32_t get_vertex_input_mask() {
+ if (input_mask == 0) {
+ ERR_FAIL_COND_V(shader.is_null(), 0);
+ input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(shader);
+ }
return input_mask;
}
void clear();
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 72a42265b3..eb33f296d0 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1010,29 +1010,6 @@ RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, boo
}
{
- //needs samplers for the material (uses custom textures) create them
- Vector<RID> ids;
- ids.resize(12);
- RID *ids_ptr = ids.ptrw();
- ids_ptr[0] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[1] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[2] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[3] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[4] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[5] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[6] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[7] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[8] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[9] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[10] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[11] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
-
- RD::Uniform u(RD::UNIFORM_TYPE_SAMPLER, 8, ids);
-
- uniforms.push_back(u);
- }
-
- {
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 9;
@@ -1040,6 +1017,8 @@ RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, boo
uniforms.push_back(u);
}
+ uniforms.append_array(material_storage->get_default_sampler_uniforms(SAMPLERS_BINDING_FIRST_INDEX));
+
RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader, BASE_UNIFORM_SET);
if (p_backbuffer) {
texture_storage->render_target_set_backbuffer_uniform_set(p_to_render_target, uniform_set);
@@ -2302,6 +2281,8 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
global_defines += "#define MAX_LIGHTS " + itos(DEFAULT_MAX_LIGHTS_PER_RENDER) + "\n";
}
+ global_defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n";
+
state.light_uniforms = memnew_arr(LightUniform, state.max_lights_per_render);
Vector<String> variants;
//non light variants
@@ -2471,7 +2452,6 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
actions.custom_samplers["TEXTURE"] = "texture_sampler";
actions.custom_samplers["NORMAL_TEXTURE"] = "texture_sampler";
actions.custom_samplers["SPECULAR_SHININESS_TEXTURE"] = "texture_sampler";
- actions.sampler_array_name = "material_samplers";
actions.base_texture_binding_index = 1;
actions.texture_layout_set = MATERIAL_UNIFORM_SET;
actions.base_uniform_string = "material.";
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index 7dea4a1a65..1116bed6e4 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -48,6 +48,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
CANVAS_TEXTURE_UNIFORM_SET = 3,
};
+ const int SAMPLERS_BINDING_FIRST_INDEX = 10;
+
enum ShaderVariant {
SHADER_VARIANT_QUAD,
SHADER_VARIANT_NINEPATCH,
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 16bc36f4b8..646bdc5436 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -659,16 +659,18 @@ void RendererSceneRenderRD::_disable_clear_request(const RenderDataRD *p_render_
texture_storage->render_target_disable_clear_request(p_render_data->render_buffers->get_render_target());
}
-void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) {
+void RendererSceneRenderRD::_render_buffers_debug_draw(const RenderDataRD *p_render_data) {
+ RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
- ERR_FAIL_COND(p_render_buffers.is_null());
+ Ref<RenderSceneBuffersRD> rb = p_render_data->render_buffers;
+ ERR_FAIL_COND(rb.is_null());
- RID render_target = p_render_buffers->get_render_target();
+ RID render_target = rb->get_render_target();
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SHADOW_ATLAS) {
- if (p_shadow_atlas.is_valid()) {
- RID shadow_atlas_texture = RendererRD::LightStorage::get_singleton()->shadow_atlas_get_texture(p_shadow_atlas);
+ if (p_render_data->shadow_atlas.is_valid()) {
+ RID shadow_atlas_texture = RendererRD::LightStorage::get_singleton()->shadow_atlas_get_texture(p_render_data->shadow_atlas);
if (shadow_atlas_texture.is_null()) {
shadow_atlas_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_BLACK);
@@ -683,8 +685,27 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD>
if (RendererRD::LightStorage::get_singleton()->directional_shadow_get_texture().is_valid()) {
RID shadow_atlas_texture = RendererRD::LightStorage::get_singleton()->directional_shadow_get_texture();
Size2i rtsize = texture_storage->render_target_get_size(render_target);
+ RID dest_fb = texture_storage->render_target_get_rd_framebuffer(render_target);
+
+ // Determine our display size, try and keep square by using the smallest edge.
+ Size2i size = 2 * rtsize / 3;
+ if (size.x < size.y) {
+ size.y = size.x;
+ } else if (size.y < size.x) {
+ size.x = size.y;
+ }
- copy_effects->copy_to_fb_rect(shadow_atlas_texture, texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize / 2), false, true);
+ copy_effects->copy_to_fb_rect(shadow_atlas_texture, dest_fb, Rect2i(Vector2(), size), false, true);
+
+ // Visualise our view frustum to show coverage.
+ for (int i = 0; i < p_render_data->render_shadow_count; i++) {
+ RID light = p_render_data->render_shadows[i].light;
+ RID base = light_storage->light_instance_get_base_light(light);
+
+ if (light_storage->light_get_type(base) == RS::LIGHT_DIRECTIONAL) {
+ debug_effects->draw_shadow_frustum(light, p_render_data->scene_data->cam_projection, p_render_data->scene_data->cam_transform, dest_fb, Rect2(Size2(), size));
+ }
+ }
}
}
@@ -699,7 +720,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD>
}
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE) {
- RID luminance_texture = luminance->get_current_luminance_buffer(p_render_buffers);
+ RID luminance_texture = luminance->get_current_luminance_buffer(rb);
if (luminance_texture.is_valid()) {
Size2i rtsize = texture_storage->render_target_get_size(render_target);
@@ -707,21 +728,21 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(Ref<RenderSceneBuffersRD>
}
}
- if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER && _render_buffers_get_normal_texture(p_render_buffers).is_valid()) {
+ if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER && _render_buffers_get_normal_texture(rb).is_valid()) {
Size2 rtsize = texture_storage->render_target_get_size(render_target);
- copy_effects->copy_to_fb_rect(_render_buffers_get_normal_texture(p_render_buffers), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false);
+ copy_effects->copy_to_fb_rect(_render_buffers_get_normal_texture(rb), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false);
}
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_OCCLUDERS) {
- if (p_occlusion_buffer.is_valid()) {
+ if (p_render_data->occluder_debug_tex.is_valid()) {
Size2i rtsize = texture_storage->render_target_get_size(render_target);
- copy_effects->copy_to_fb_rect(texture_storage->texture_get_rd_texture(p_occlusion_buffer), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize), true, false);
+ copy_effects->copy_to_fb_rect(texture_storage->texture_get_rd_texture(p_render_data->occluder_debug_tex), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2i(Vector2(), rtsize), true, false);
}
}
- if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS && _render_buffers_get_velocity_texture(p_render_buffers).is_valid()) {
+ if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS && _render_buffers_get_velocity_texture(rb).is_valid()) {
Size2i rtsize = texture_storage->render_target_get_size(render_target);
- copy_effects->copy_to_fb_rect(_render_buffers_get_velocity_texture(p_render_buffers), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false);
+ copy_effects->copy_to_fb_rect(_render_buffers_get_velocity_texture(rb), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false);
}
}
@@ -1293,6 +1314,7 @@ void RendererSceneRenderRD::init() {
bool can_use_vrs = is_vrs_supported();
bokeh_dof = memnew(RendererRD::BokehDOF(!can_use_storage));
copy_effects = memnew(RendererRD::CopyEffects(!can_use_storage));
+ debug_effects = memnew(RendererRD::DebugEffects);
luminance = memnew(RendererRD::Luminance(!can_use_storage));
tone_mapper = memnew(RendererRD::ToneMapper);
if (can_use_vrs) {
@@ -1314,6 +1336,9 @@ RendererSceneRenderRD::~RendererSceneRenderRD() {
if (copy_effects) {
memdelete(copy_effects);
}
+ if (debug_effects) {
+ memdelete(debug_effects);
+ }
if (luminance) {
memdelete(luminance);
}
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index 7c43021eb0..4bce6a172e 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -37,6 +37,7 @@
#include "servers/rendering/renderer_rd/cluster_builder_rd.h"
#include "servers/rendering/renderer_rd/effects/bokeh_dof.h"
#include "servers/rendering/renderer_rd/effects/copy_effects.h"
+#include "servers/rendering/renderer_rd/effects/debug_effects.h"
#include "servers/rendering/renderer_rd/effects/fsr.h"
#include "servers/rendering/renderer_rd/effects/luminance.h"
#include "servers/rendering/renderer_rd/effects/tone_mapper.h"
@@ -106,6 +107,7 @@ protected:
RendererRD::ForwardIDStorage *forward_id_storage = nullptr;
RendererRD::BokehDOF *bokeh_dof = nullptr;
RendererRD::CopyEffects *copy_effects = nullptr;
+ RendererRD::DebugEffects *debug_effects = nullptr;
RendererRD::Luminance *luminance = nullptr;
RendererRD::ToneMapper *tone_mapper = nullptr;
RendererRD::FSR *fsr = nullptr;
@@ -128,7 +130,7 @@ protected:
virtual void setup_render_buffer_data(Ref<RenderSceneBuffersRD> p_render_buffers) = 0;
virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_color) = 0;
- virtual void _render_buffers_debug_draw(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer);
+ virtual void _render_buffers_debug_draw(const RenderDataRD *p_render_data);
virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) = 0;
virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0;
diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp
index c85ece6366..d3814f175f 100644
--- a/servers/rendering/renderer_rd/shader_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_rd.cpp
@@ -138,7 +138,7 @@ void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, con
RID ShaderRD::version_create() {
//initialize() was never called
- ERR_FAIL_COND_V(variant_defines.size() == 0, RID());
+ ERR_FAIL_COND_V(group_to_variant_map.size() == 0, RID());
Version version;
version.dirty = true;
@@ -148,11 +148,20 @@ RID ShaderRD::version_create() {
return version_owner.make_rid(version);
}
+void ShaderRD::_initialize_version(Version *p_version) {
+ _clear_version(p_version);
+
+ p_version->valid = false;
+ p_version->dirty = false;
+
+ p_version->variants = memnew_arr(RID, variant_defines.size());
+}
+
void ShaderRD::_clear_version(Version *p_version) {
- //clear versions if they exist
+ // Clear versions if they exist.
if (p_version->variants) {
for (int i = 0; i < variant_defines.size(); i++) {
- if (variants_enabled[i]) {
+ if (variants_enabled[i] && group_enabled[variant_defines[i].group]) {
RD::get_singleton()->free(p_version->variants[i]);
}
}
@@ -171,7 +180,7 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c
case StageTemplate::Chunk::TYPE_VERSION_DEFINES: {
builder.append("\n"); //make sure defines begin at newline
builder.append(general_defines.get_data());
- builder.append(variant_defines[p_variant].get_data());
+ builder.append(variant_defines[p_variant].text.get_data());
for (int j = 0; j < p_version->custom_defines.size(); j++) {
builder.append(p_version->custom_defines[j].get_data());
}
@@ -211,9 +220,11 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c
}
}
-void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
- if (!variants_enabled[p_variant]) {
- return; //variant is disabled, return
+void ShaderRD::_compile_variant(uint32_t p_variant, const CompileData *p_data) {
+ uint32_t variant = group_to_variant_map[p_data->group][p_variant];
+
+ if (!variants_enabled[variant]) {
+ return; // Variant is disabled, return.
}
Vector<RD::ShaderStageSPIRVData> stages;
@@ -227,7 +238,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
//vertex stage
StringBuilder builder;
- _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX]);
+ _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_VERTEX]);
current_source = builder.as_string();
RD::ShaderStageSPIRVData stage;
@@ -245,7 +256,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
current_stage = RD::SHADER_STAGE_FRAGMENT;
StringBuilder builder;
- _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT]);
+ _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_FRAGMENT]);
current_source = builder.as_string();
RD::ShaderStageSPIRVData stage;
@@ -263,7 +274,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
current_stage = RD::SHADER_STAGE_COMPUTE;
StringBuilder builder;
- _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_COMPUTE]);
+ _build_variant_code(builder, variant, p_data->version, stage_templates[STAGE_TYPE_COMPUTE]);
current_source = builder.as_string();
@@ -279,7 +290,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
if (!build_ok) {
MutexLock lock(variant_set_mutex); //properly print the errors
- ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment")) + " shader, variant #" + itos(p_variant) + " (" + variant_defines[p_variant].get_data() + ").");
+ ERR_PRINT("Error compiling " + String(current_stage == RD::SHADER_STAGE_COMPUTE ? "Compute " : (current_stage == RD::SHADER_STAGE_VERTEX ? "Vertex" : "Fragment")) + " shader, variant #" + itos(variant) + " (" + variant_defines[variant].text.get_data() + ").");
ERR_PRINT(error);
#ifdef DEBUG_ENABLED
@@ -288,15 +299,15 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
return;
}
- Vector<uint8_t> shader_data = RD::get_singleton()->shader_compile_binary_from_spirv(stages, name + ":" + itos(p_variant));
+ Vector<uint8_t> shader_data = RD::get_singleton()->shader_compile_binary_from_spirv(stages, name + ":" + itos(variant));
ERR_FAIL_COND(shader_data.size() == 0);
- RID shader = RD::get_singleton()->shader_create_from_bytecode(shader_data);
{
MutexLock lock(variant_set_mutex);
- p_version->variants[p_variant] = shader;
- p_version->variant_data[p_variant] = shader_data;
+
+ p_data->version->variants[variant] = RD::get_singleton()->shader_create_from_bytecode(shader_data, p_data->version->variants[variant]);
+ p_data->version->variant_data[variant] = shader_data;
}
}
@@ -382,11 +393,11 @@ String ShaderRD::_version_get_sha1(Version *p_version) const {
}
static const char *shader_file_header = "GDSC";
-static const uint32_t cache_file_version = 2;
+static const uint32_t cache_file_version = 3;
-bool ShaderRD::_load_from_cache(Version *p_version) {
+bool ShaderRD::_load_from_cache(Version *p_version, int p_group) {
String sha1 = _version_get_sha1(p_version);
- String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache";
+ String path = shader_cache_dir.path_join(name).path_join(group_sha256[p_group]).path_join(sha1) + ".cache";
Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ);
if (f.is_null()) {
@@ -404,12 +415,13 @@ bool ShaderRD::_load_from_cache(Version *p_version) {
uint32_t variant_count = f->get_32();
- ERR_FAIL_COND_V(variant_count != (uint32_t)variant_defines.size(), false); //should not happen but check
+ ERR_FAIL_COND_V(variant_count != (uint32_t)group_to_variant_map[p_group].size(), false); //should not happen but check
for (uint32_t i = 0; i < variant_count; i++) {
+ int variant_id = group_to_variant_map[p_group][i];
uint32_t variant_size = f->get_32();
- ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[i], false);
- if (!variants_enabled[i]) {
+ ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[variant_id], false);
+ if (!variants_enabled[variant_id]) {
continue;
}
Vector<uint8_t> variant_bytes;
@@ -419,25 +431,28 @@ bool ShaderRD::_load_from_cache(Version *p_version) {
ERR_FAIL_COND_V(br != variant_size, false);
- p_version->variant_data[i] = variant_bytes;
+ p_version->variant_data[variant_id] = variant_bytes;
}
for (uint32_t i = 0; i < variant_count; i++) {
- if (!variants_enabled[i]) {
+ int variant_id = group_to_variant_map[p_group][i];
+ if (!variants_enabled[variant_id]) {
MutexLock lock(variant_set_mutex);
- p_version->variants[i] = RID();
+ p_version->variants[variant_id] = RID();
continue;
}
- RID shader = RD::get_singleton()->shader_create_from_bytecode(p_version->variant_data[i]);
- if (shader.is_null()) {
- for (uint32_t j = 0; j < i; j++) {
- RD::get_singleton()->free(p_version->variants[i]);
- }
- ERR_FAIL_COND_V(shader.is_null(), false);
- }
{
MutexLock lock(variant_set_mutex);
- p_version->variants[i] = shader;
+ RID shader = RD::get_singleton()->shader_create_from_bytecode(p_version->variant_data[variant_id], p_version->variants[variant_id]);
+ if (shader.is_null()) {
+ for (uint32_t j = 0; j < i; j++) {
+ int variant_free_id = group_to_variant_map[p_group][j];
+ RD::get_singleton()->free(p_version->variants[variant_free_id]);
+ }
+ ERR_FAIL_COND_V(shader.is_null(), false);
+ }
+
+ p_version->variants[variant_id] = shader;
}
}
@@ -447,66 +462,84 @@ bool ShaderRD::_load_from_cache(Version *p_version) {
return true;
}
-void ShaderRD::_save_to_cache(Version *p_version) {
+void ShaderRD::_save_to_cache(Version *p_version, int p_group) {
String sha1 = _version_get_sha1(p_version);
- String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache";
+ String path = shader_cache_dir.path_join(name).path_join(group_sha256[p_group]).path_join(sha1) + ".cache";
Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE);
ERR_FAIL_COND(f.is_null());
f->store_buffer((const uint8_t *)shader_file_header, 4);
- f->store_32(cache_file_version); //file version
- uint32_t variant_count = variant_defines.size();
- f->store_32(variant_count); //variant count
-
+ f->store_32(cache_file_version); // File version.
+ uint32_t variant_count = group_to_variant_map[p_group].size();
+ f->store_32(variant_count); // Variant count.
for (uint32_t i = 0; i < variant_count; i++) {
- f->store_32(p_version->variant_data[i].size()); //stage count
- f->store_buffer(p_version->variant_data[i].ptr(), p_version->variant_data[i].size());
+ int variant_id = group_to_variant_map[p_group][i];
+ f->store_32(p_version->variant_data[variant_id].size()); // Stage count.
+ f->store_buffer(p_version->variant_data[variant_id].ptr(), p_version->variant_data[variant_id].size());
}
}
-void ShaderRD::_compile_version(Version *p_version) {
- _clear_version(p_version);
+void ShaderRD::_allocate_placeholders(Version *p_version, int p_group) {
+ for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
+ int variant_id = group_to_variant_map[p_group][i];
+ RID shader = RD::get_singleton()->shader_create_placeholder();
+ {
+ MutexLock lock(variant_set_mutex);
+ p_version->variants[variant_id] = shader;
+ }
+ }
+}
- p_version->valid = false;
- p_version->dirty = false;
+// Try to compile all variants for a given group.
+// Will skip variants that are disabled.
+void ShaderRD::_compile_version(Version *p_version, int p_group) {
+ if (!group_enabled[p_group]) {
+ return;
+ }
- p_version->variants = memnew_arr(RID, variant_defines.size());
typedef Vector<uint8_t> ShaderStageData;
p_version->variant_data = memnew_arr(ShaderStageData, variant_defines.size());
+ p_version->dirty = false;
+
if (shader_cache_dir_valid) {
- if (_load_from_cache(p_version)) {
+ if (_load_from_cache(p_version, p_group)) {
return;
}
}
-#if 1
+ CompileData compile_data;
+ compile_data.version = p_version;
+ compile_data.group = p_group;
- WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, p_version, variant_defines.size(), -1, true, SNAME("ShaderCompilation"));
+#if 1
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, &compile_data, group_to_variant_map[p_group].size(), -1, true, SNAME("ShaderCompilation"));
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
#else
- for (int i = 0; i < variant_defines.size(); i++) {
- _compile_variant(i, p_version);
+ for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
+ _compile_variant(i, &compile_data);
}
#endif
bool all_valid = true;
- for (int i = 0; i < variant_defines.size(); i++) {
- if (!variants_enabled[i]) {
- continue; //disabled
+
+ for (uint32_t i = 0; i < group_to_variant_map[p_group].size(); i++) {
+ int variant_id = group_to_variant_map[p_group][i];
+ if (!variants_enabled[variant_id]) {
+ continue; // Disabled.
}
- if (p_version->variants[i].is_null()) {
+ if (p_version->variants[variant_id].is_null()) {
all_valid = false;
break;
}
}
if (!all_valid) {
- //clear versions if they exist
+ // Clear versions if they exist.
for (int i = 0; i < variant_defines.size(); i++) {
- if (!variants_enabled[i]) {
- continue; //disabled
+ if (!variants_enabled[i] || !group_enabled[variant_defines[i].group]) {
+ continue; // Disabled.
}
if (!p_version->variants[i].is_null()) {
RD::get_singleton()->free(p_version->variants[i]);
@@ -520,8 +553,8 @@ void ShaderRD::_compile_version(Version *p_version) {
p_version->variant_data = nullptr;
return;
} else if (shader_cache_dir_valid) {
- //save shader cache
- _save_to_cache(p_version);
+ // Save shader cache.
+ _save_to_cache(p_version, p_group);
}
memdelete_arr(p_version->variant_data); //clear stages
@@ -550,7 +583,14 @@ void ShaderRD::version_set_code(RID p_version, const HashMap<String, String> &p_
version->dirty = true;
if (version->initialize_needed) {
- _compile_version(version);
+ _initialize_version(version);
+ for (int i = 0; i < group_enabled.size(); i++) {
+ if (!group_enabled[i]) {
+ _allocate_placeholders(version, i);
+ continue;
+ }
+ _compile_version(version, i);
+ }
version->initialize_needed = false;
}
}
@@ -576,7 +616,14 @@ void ShaderRD::version_set_compute_code(RID p_version, const HashMap<String, Str
version->dirty = true;
if (version->initialize_needed) {
- _compile_version(version);
+ _initialize_version(version);
+ for (int i = 0; i < group_enabled.size(); i++) {
+ if (!group_enabled[i]) {
+ _allocate_placeholders(version, i);
+ continue;
+ }
+ _compile_version(version, i);
+ }
version->initialize_needed = false;
}
}
@@ -586,7 +633,14 @@ bool ShaderRD::version_is_valid(RID p_version) {
ERR_FAIL_COND_V(!version, false);
if (version->dirty) {
- _compile_version(version);
+ _initialize_version(version);
+ for (int i = 0; i < group_enabled.size(); i++) {
+ if (!group_enabled[i]) {
+ _allocate_placeholders(version, i);
+ continue;
+ }
+ _compile_version(version, i);
+ }
}
return version->valid;
@@ -615,6 +669,29 @@ bool ShaderRD::is_variant_enabled(int p_variant) const {
return variants_enabled[p_variant];
}
+void ShaderRD::enable_group(int p_group) {
+ ERR_FAIL_INDEX(p_group, group_enabled.size());
+
+ if (group_enabled[p_group]) {
+ // Group already enabled, do nothing.
+ return;
+ }
+
+ group_enabled.write[p_group] = true;
+
+ // Compile all versions again to include the new group.
+ List<RID> all_versions;
+ version_owner.get_owned_list(&all_versions);
+ for (int i = 0; i < all_versions.size(); i++) {
+ Version *version = version_owner.get_or_null(all_versions[i]);
+ _compile_version(version, p_group);
+ }
+}
+
+bool ShaderRD::is_group_enabled(int p_group) const {
+ return group_enabled[p_group];
+}
+
bool ShaderRD::shader_cache_cleanup_on_start = false;
ShaderRD::ShaderRD() {
@@ -639,24 +716,38 @@ void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String
general_defines = p_general_defines.utf8();
+ // When initialized this way, there is just one group and its always enabled.
+ group_to_variant_map.insert(0, LocalVector<int>{});
+ group_enabled.push_back(true);
+
for (int i = 0; i < p_variant_defines.size(); i++) {
- variant_defines.push_back(p_variant_defines[i].utf8());
+ variant_defines.push_back(VariantDefine(0, p_variant_defines[i], true));
variants_enabled.push_back(true);
+ group_to_variant_map[0].push_back(i);
}
if (!shader_cache_dir.is_empty()) {
+ group_sha256.resize(1);
+ _initialize_cache();
+ }
+}
+
+void ShaderRD::_initialize_cache() {
+ for (const KeyValue<int, LocalVector<int>> &E : group_to_variant_map) {
StringBuilder hash_build;
hash_build.append("[base_hash]");
hash_build.append(base_sha256);
hash_build.append("[general_defines]");
hash_build.append(general_defines.get_data());
- for (int i = 0; i < variant_defines.size(); i++) {
- hash_build.append("[variant_defines:" + itos(i) + "]");
- hash_build.append(variant_defines[i].get_data());
+ hash_build.append("[group_id]");
+ hash_build.append(itos(E.key));
+ for (uint32_t i = 0; i < E.value.size(); i++) {
+ hash_build.append("[variant_defines:" + itos(E.value[i]) + "]");
+ hash_build.append(variant_defines[E.value[i]].text.get_data());
}
- base_sha256 = hash_build.as_string().sha256_text();
+ group_sha256[E.key] = hash_build.as_string().sha256_text();
Ref<DirAccess> d = DirAccess::open(shader_cache_dir);
ERR_FAIL_COND(d.is_null());
@@ -666,17 +757,58 @@ void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String
d->change_dir(name);
}
- //erase other versions?
+ // Erase other versions?
if (shader_cache_cleanup_on_start) {
}
//
- if (d->change_dir(base_sha256) != OK) {
- Error err = d->make_dir(base_sha256);
+ if (d->change_dir(group_sha256[E.key]) != OK) {
+ Error err = d->make_dir(group_sha256[E.key]);
ERR_FAIL_COND(err != OK);
}
shader_cache_dir_valid = true;
- print_verbose("Shader '" + name + "' SHA256: " + base_sha256);
+ print_verbose("Shader '" + name + "' (group " + itos(E.key) + ") SHA256: " + group_sha256[E.key]);
+ }
+}
+
+// Same as above, but allows specifying shader compilation groups.
+void ShaderRD::initialize(const Vector<VariantDefine> &p_variant_defines, const String &p_general_defines) {
+ ERR_FAIL_COND(variant_defines.size());
+ ERR_FAIL_COND(p_variant_defines.size() == 0);
+
+ general_defines = p_general_defines.utf8();
+
+ int max_group_id = 0;
+
+ for (int i = 0; i < p_variant_defines.size(); i++) {
+ // Fill variant array.
+ variant_defines.push_back(p_variant_defines[i]);
+ variants_enabled.push_back(true);
+
+ // Map variant array index to group id, so we can iterate over groups later.
+ if (!group_to_variant_map.has(p_variant_defines[i].group)) {
+ group_to_variant_map.insert(p_variant_defines[i].group, LocalVector<int>{});
+ }
+ group_to_variant_map[p_variant_defines[i].group].push_back(i);
+
+ // Track max size.
+ if (p_variant_defines[i].group > max_group_id) {
+ max_group_id = p_variant_defines[i].group;
+ }
+ }
+
+ // Set all to groups to false, then enable those that should be default.
+ group_enabled.resize_zeroed(max_group_id + 1);
+ bool *enabled_ptr = group_enabled.ptrw();
+ for (int i = 0; i < p_variant_defines.size(); i++) {
+ if (p_variant_defines[i].default_enabled) {
+ enabled_ptr[p_variant_defines[i].group] = true;
+ }
+ }
+
+ if (!shader_cache_dir.is_empty()) {
+ group_sha256.resize(max_group_id + 1);
+ _initialize_cache();
}
}
diff --git a/servers/rendering/renderer_rd/shader_rd.h b/servers/rendering/renderer_rd/shader_rd.h
index d0871ca16c..01eb99f7a2 100644
--- a/servers/rendering/renderer_rd/shader_rd.h
+++ b/servers/rendering/renderer_rd/shader_rd.h
@@ -41,10 +41,26 @@
#include "servers/rendering_server.h"
class ShaderRD {
+public:
+ struct VariantDefine {
+ int group = 0;
+ CharString text;
+ bool default_enabled = true;
+ VariantDefine(){};
+ VariantDefine(int p_group, const String &p_text, bool p_default_enabled) {
+ group = p_group;
+ default_enabled = p_default_enabled;
+ text = p_text.utf8();
+ }
+ };
+
+private:
//versions
CharString general_defines;
- Vector<CharString> variant_defines;
+ Vector<VariantDefine> variant_defines;
Vector<bool> variants_enabled;
+ HashMap<int, LocalVector<int>> group_to_variant_map;
+ Vector<bool> group_enabled;
struct Version {
CharString uniforms;
@@ -55,7 +71,7 @@ class ShaderRD {
Vector<CharString> custom_defines;
Vector<uint8_t> *variant_data = nullptr;
- RID *variants = nullptr; //same size as version defines
+ RID *variants = nullptr; // Same size as variant defines.
bool valid;
bool dirty;
@@ -64,10 +80,17 @@ class ShaderRD {
Mutex variant_set_mutex;
- void _compile_variant(uint32_t p_variant, Version *p_version);
+ struct CompileData {
+ Version *version;
+ int group = 0;
+ };
+
+ void _compile_variant(uint32_t p_variant, const CompileData *p_data);
+ void _initialize_version(Version *p_version);
void _clear_version(Version *p_version);
- void _compile_version(Version *p_version);
+ void _compile_version(Version *p_version, int p_group);
+ void _allocate_placeholders(Version *p_version, int p_group);
RID_Owner<Version> version_owner;
@@ -97,6 +120,7 @@ class ShaderRD {
CharString base_compute_defines;
String base_sha256;
+ LocalVector<String> group_sha256;
static String shader_cache_dir;
static bool shader_cache_cleanup_on_start;
@@ -119,8 +143,9 @@ class ShaderRD {
void _add_stage(const char *p_code, StageType p_stage_type);
String _version_get_sha1(Version *p_version) const;
- bool _load_from_cache(Version *p_version);
- void _save_to_cache(Version *p_version);
+ bool _load_from_cache(Version *p_version, int p_group);
+ void _save_to_cache(Version *p_version, int p_group);
+ void _initialize_cache();
protected:
ShaderRD();
@@ -140,7 +165,14 @@ public:
ERR_FAIL_COND_V(!version, RID());
if (version->dirty) {
- _compile_version(version);
+ _initialize_version(version);
+ for (int i = 0; i < group_enabled.size(); i++) {
+ if (!group_enabled[i]) {
+ _allocate_placeholders(version, i);
+ continue;
+ }
+ _compile_version(version, i);
+ }
}
if (!version->valid) {
@@ -154,9 +186,14 @@ public:
bool version_free(RID p_version);
+ // Enable/disable variants for things that you know won't be used at engine initialization time .
void set_variant_enabled(int p_variant, bool p_enabled);
bool is_variant_enabled(int p_variant) const;
+ // Enable/disable groups for things that might be enabled at run time.
+ void enable_group(int p_group);
+ bool is_group_enabled(int p_group) const;
+
static void set_shader_cache_dir(const String &p_dir);
static void set_shader_cache_save_compressed(bool p_enable);
static void set_shader_cache_save_compressed_zstd(bool p_enable);
@@ -165,6 +202,8 @@ public:
RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version);
void initialize(const Vector<String> &p_variant_defines, const String &p_general_defines = "");
+ void initialize(const Vector<VariantDefine> &p_variant_defines, const String &p_general_defines = "");
+
virtual ~ShaderRD();
};
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index 7deeacf86e..6270450c15 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -237,7 +237,7 @@ vec2 screen_uv_to_sdf(vec2 p_uv) {
float texture_sdf(vec2 p_sdf) {
vec2 uv = p_sdf * canvas_data.sdf_to_tex.xy + canvas_data.sdf_to_tex.zw;
- float d = texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv).r;
+ float d = texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv).r;
d *= SDF_MAX_LENGTH;
return d * canvas_data.tex_to_sdf;
}
@@ -247,8 +247,8 @@ vec2 texture_sdf_normal(vec2 p_sdf) {
const float EPSILON = 0.001;
return normalize(vec2(
- texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv + vec2(EPSILON, 0.0)).r - texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv - vec2(EPSILON, 0.0)).r,
- texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv + vec2(0.0, EPSILON)).r - texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv - vec2(0.0, EPSILON)).r));
+ texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv + vec2(EPSILON, 0.0)).r - texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv - vec2(EPSILON, 0.0)).r,
+ texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv + vec2(0.0, EPSILON)).r - texture(sampler2D(sdf_texture, SAMPLER_LINEAR_CLAMP), uv - vec2(0.0, EPSILON)).r));
}
vec2 sdf_to_screen_uv(vec2 p_sdf) {
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index a904f4e0a6..2dd5abb75f 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -30,19 +30,6 @@
#define FLAGS_FLIP_H (1 << 30)
#define FLAGS_FLIP_V (1 << 31)
-#define SAMPLER_NEAREST_CLAMP 0
-#define SAMPLER_LINEAR_CLAMP 1
-#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
-#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
-#define SAMPLER_NEAREST_REPEAT 6
-#define SAMPLER_LINEAR_REPEAT 7
-#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
-#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
-
// Push Constant
layout(push_constant, std430) uniform DrawData {
@@ -140,7 +127,7 @@ layout(set = 0, binding = 5) uniform sampler shadow_sampler;
layout(set = 0, binding = 6) uniform texture2D color_buffer;
layout(set = 0, binding = 7) uniform texture2D sdf_texture;
-layout(set = 0, binding = 8) uniform sampler material_samplers[12];
+#include "samplers_inc.glsl"
layout(set = 0, binding = 9, std430) restrict readonly buffer GlobalShaderUniformData {
vec4 data[];
diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
index 1626244b0a..1e01d94533 100644
--- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
@@ -263,7 +263,9 @@ void main() {
// Schlick term.
float metallic = texelFetch(source_metallic, ssC << 1, 0).w;
- float f0 = mix(0.04, 0.37, metallic); // The default value of R0 is 0.04 and the maximum value is considered to be Germanium with R0 value of 0.37
+ // F0 is the reflectance of normally incident light (perpendicular to the surface).
+ // Dielectric materials have a widely accepted default value of 0.04. We assume that metals reflect all light, so their F0 is 1.0.
+ float f0 = mix(0.04, 1.0, metallic);
float m = clamp(1.0 - dot(normal, -view_dir), 0.0, 1.0);
float m2 = m * m;
m = m2 * m2 * m; // pow(m,5)
diff --git a/servers/rendering/renderer_rd/shaders/effects/shadow_frustum.glsl b/servers/rendering/renderer_rd/shaders/effects/shadow_frustum.glsl
new file mode 100644
index 0000000000..11ec7ec59e
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/effects/shadow_frustum.glsl
@@ -0,0 +1,41 @@
+/* clang-format off */
+#[vertex]
+
+#version 450
+
+#VERSION_DEFINES
+
+/* clang-format on */
+
+layout(push_constant, std430) uniform Info {
+ mat4 mvp;
+ vec4 color;
+}
+info;
+
+layout(location = 0) in vec3 vertex_attrib;
+
+void main() {
+ vec4 vertex = info.mvp * vec4(vertex_attrib, 1.0);
+ vertex.xyz /= vertex.w;
+ gl_Position = vec4(vertex.xy, 0.0, 1.0);
+}
+
+/* clang-format off */
+#[fragment]
+
+#version 450
+
+#VERSION_DEFINES
+
+layout(push_constant, std430) uniform Info {
+ mat4 mvp;
+ vec4 color;
+}
+info;
+
+layout(location = 0) out vec4 frag_color;
+
+void main() {
+ frag_color = info.color;
+}
diff --git a/servers/rendering/renderer_rd/shaders/environment/sky.glsl b/servers/rendering/renderer_rd/shaders/environment/sky.glsl
index b38aa0ad75..d605917acc 100644
--- a/servers/rendering/renderer_rd/shaders/environment/sky.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/sky.glsl
@@ -62,20 +62,7 @@ layout(push_constant, std430) uniform Params {
}
params;
-#define SAMPLER_NEAREST_CLAMP 0
-#define SAMPLER_LINEAR_CLAMP 1
-#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
-#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
-#define SAMPLER_NEAREST_REPEAT 6
-#define SAMPLER_LINEAR_REPEAT 7
-#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
-#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
-
-layout(set = 0, binding = 0) uniform sampler material_samplers[12];
+#include "../samplers_inc.glsl"
layout(set = 0, binding = 1, std430) restrict readonly buffer GlobalShaderUniformData {
vec4 data[];
@@ -177,7 +164,7 @@ vec4 volumetric_fog_process(vec2 screen_uv) {
vec3 fog_pos = vec3(screen_uv, 1.0);
#endif
- return texture(sampler3D(volumetric_fog_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), fog_pos);
+ return texture(sampler3D(volumetric_fog_texture, SAMPLER_LINEAR_CLAMP), fog_pos);
}
vec4 fog_process(vec3 view, vec3 sky_color) {
@@ -231,27 +218,27 @@ void main() {
#ifdef USE_CUBEMAP_PASS
#ifdef USES_HALF_RES_COLOR
- half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal) / params.luminance_multiplier;
+ half_res_color = texture(samplerCube(half_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal) / params.luminance_multiplier;
#endif
#ifdef USES_QUARTER_RES_COLOR
- quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal) / params.luminance_multiplier;
+ quarter_res_color = texture(samplerCube(quarter_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal) / params.luminance_multiplier;
#endif
#else
#ifdef USES_HALF_RES_COLOR
#ifdef USE_MULTIVIEW
- half_res_color = textureLod(sampler2DArray(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(uv, ViewIndex), 0.0) / params.luminance_multiplier;
+ half_res_color = textureLod(sampler2DArray(half_res, SAMPLER_LINEAR_CLAMP), vec3(uv, ViewIndex), 0.0) / params.luminance_multiplier;
#else
- half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) / params.luminance_multiplier;
+ half_res_color = textureLod(sampler2D(half_res, SAMPLER_LINEAR_CLAMP), uv, 0.0) / params.luminance_multiplier;
#endif // USE_MULTIVIEW
#endif // USES_HALF_RES_COLOR
#ifdef USES_QUARTER_RES_COLOR
#ifdef USE_MULTIVIEW
- quarter_res_color = textureLod(sampler2DArray(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(uv, ViewIndex), 0.0) / params.luminance_multiplier;
+ quarter_res_color = textureLod(sampler2DArray(quarter_res, SAMPLER_LINEAR_CLAMP), vec3(uv, ViewIndex), 0.0) / params.luminance_multiplier;
#else
- quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) / params.luminance_multiplier;
+ quarter_res_color = textureLod(sampler2D(quarter_res, SAMPLER_LINEAR_CLAMP), uv, 0.0) / params.luminance_multiplier;
#endif // USE_MULTIVIEW
#endif // USES_QUARTER_RES_COLOR
diff --git a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl
index 8e4f5762fd..6ba1fe2819 100644
--- a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog.glsl
@@ -6,19 +6,6 @@
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
-#define SAMPLER_NEAREST_CLAMP 0
-#define SAMPLER_LINEAR_CLAMP 1
-#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
-#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
-#define SAMPLER_NEAREST_REPEAT 6
-#define SAMPLER_LINEAR_REPEAT 7
-#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
-#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
-
#define DENSITY_SCALE 1024.0
#include "../cluster_data_inc.glsl"
@@ -26,7 +13,7 @@ layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
#define M_PI 3.14159265359
-layout(set = 0, binding = 1) uniform sampler material_samplers[12];
+#include "../samplers_inc.glsl"
layout(set = 0, binding = 2, std430) restrict readonly buffer GlobalShaderUniformData {
vec4 data[];
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
index b38f0629c0..9214a953aa 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
@@ -661,7 +661,7 @@ vec4 volumetric_fog_process(vec2 screen_uv, float z) {
fog_pos.z = pow(fog_pos.z, implementation_data.volumetric_fog_detail_spread);
}
- return texture(sampler3D(volumetric_fog_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), fog_pos);
+ return texture(sampler3D(volumetric_fog_texture, SAMPLER_LINEAR_CLAMP), fog_pos);
}
vec4 fog_process(vec3 vertex) {
@@ -675,10 +675,10 @@ vec4 fog_process(vec3 vertex) {
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
float lod, blend;
blend = modf(mip_level * MAX_ROUGHNESS_LOD, lod);
- sky_fog_color = texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(cube_view, lod)).rgb;
- sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(cube_view, lod + 1)).rgb, blend);
+ sky_fog_color = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb;
+ sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend);
#else
- sky_fog_color = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb;
+ sky_fog_color = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective);
}
@@ -875,11 +875,15 @@ void fragment_shader(in SceneData scene_data) {
alpha = compute_alpha_antialiasing_edge(alpha, alpha_texture_coordinate, alpha_antialiasing_edge);
#endif // ALPHA_ANTIALIASING_EDGE_USED
+#ifdef MODE_RENDER_DEPTH
#ifdef USE_OPAQUE_PREPASS
+#ifndef ALPHA_SCISSOR_USED
if (alpha < scene_data.opaque_prepass_threshold) {
discard;
}
+#endif // !ALPHA_SCISSOR_USED
#endif // USE_OPAQUE_PREPASS
+#endif // MODE_RENDER_DEPTH
#endif // !USE_SHADOW_TO_OPACITY
@@ -1124,11 +1128,11 @@ void fragment_shader(in SceneData scene_data) {
float lod, blend;
blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod);
- specular_light = texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ref_vec, lod)).rgb;
- specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ref_vec, lod + 1)).rgb, blend);
+ specular_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
+ specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
#else
- specular_light = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
+ specular_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
specular_light *= scene_data.IBL_exposure_normalization;
@@ -1148,9 +1152,9 @@ void fragment_shader(in SceneData scene_data) {
if (scene_data.use_ambient_cubemap) {
vec3 ambient_dir = scene_data.radiance_inverse_xform * normal;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
- vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
+ vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
#else
- vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ambient_dir, MAX_ROUGHNESS_LOD).rgb;
+ vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
cubemap_ambient *= scene_data.IBL_exposure_normalization;
ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
@@ -1181,11 +1185,11 @@ void fragment_shader(in SceneData scene_data) {
float lod, blend;
blend = modf(roughness_lod, lod);
- vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ref_vec, lod)).rgb;
- clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ref_vec, lod + 1)).rgb, blend);
+ vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
+ clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
#else
- vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ref_vec, roughness_lod).rgb;
+ vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
@@ -1231,10 +1235,10 @@ void fragment_shader(in SceneData scene_data) {
if (uses_sh) {
uvw.z *= 4.0; //SH textures use 4 times more data
- vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
- vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
- vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
- vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
+ vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
+ vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
+ vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
+ vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
uint idx = instances.data[instance_index].gi_offset >> 20;
vec3 n = normalize(lightmaps.data[idx].normal_xform * normal);
@@ -1253,7 +1257,7 @@ void fragment_shader(in SceneData scene_data) {
} else {
uint idx = instances.data[instance_index].gi_offset >> 20;
- ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb * lightmaps.data[idx].exposure_normalization;
+ ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[idx].exposure_normalization;
}
}
#else
@@ -1373,18 +1377,18 @@ void fragment_shader(in SceneData scene_data) {
vec2 base_coord = screen_uv;
vec2 closest_coord = base_coord;
#ifdef USE_MULTIVIEW
- float closest_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(base_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0);
+ float closest_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(base_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0);
#else // USE_MULTIVIEW
- float closest_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), base_coord, 0.0).xyz * 2.0 - 1.0);
+ float closest_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), base_coord, 0.0).xyz * 2.0 - 1.0);
#endif // USE_MULTIVIEW
for (int i = 0; i < 4; i++) {
const vec2 neighbors[4] = vec2[](vec2(-1, 0), vec2(1, 0), vec2(0, -1), vec2(0, 1));
vec2 neighbour_coord = base_coord + neighbors[i] * scene_data.screen_pixel_size;
#ifdef USE_MULTIVIEW
- float neighbour_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(neighbour_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0);
+ float neighbour_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), vec3(neighbour_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0);
#else // USE_MULTIVIEW
- float neighbour_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), neighbour_coord, 0.0).xyz * 2.0 - 1.0);
+ float neighbour_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, SAMPLER_LINEAR_CLAMP), neighbour_coord, 0.0).xyz * 2.0 - 1.0);
#endif // USE_MULTIVIEW
if (neighbour_ang > closest_ang) {
closest_ang = neighbour_ang;
@@ -1399,11 +1403,11 @@ void fragment_shader(in SceneData scene_data) {
}
#ifdef USE_MULTIVIEW
- vec4 buffer_ambient = textureLod(sampler2DArray(ambient_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(coord, ViewIndex), 0.0);
- vec4 buffer_reflection = textureLod(sampler2DArray(reflection_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(coord, ViewIndex), 0.0);
+ vec4 buffer_ambient = textureLod(sampler2DArray(ambient_buffer, SAMPLER_LINEAR_CLAMP), vec3(coord, ViewIndex), 0.0);
+ vec4 buffer_reflection = textureLod(sampler2DArray(reflection_buffer, SAMPLER_LINEAR_CLAMP), vec3(coord, ViewIndex), 0.0);
#else // USE_MULTIVIEW
- vec4 buffer_ambient = textureLod(sampler2D(ambient_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0.0);
- vec4 buffer_reflection = textureLod(sampler2D(reflection_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0.0);
+ vec4 buffer_ambient = textureLod(sampler2D(ambient_buffer, SAMPLER_LINEAR_CLAMP), coord, 0.0);
+ vec4 buffer_reflection = textureLod(sampler2D(reflection_buffer, SAMPLER_LINEAR_CLAMP), coord, 0.0);
#endif // USE_MULTIVIEW
ambient_light = mix(ambient_light, buffer_ambient.rgb, buffer_ambient.a);
@@ -1413,9 +1417,9 @@ void fragment_shader(in SceneData scene_data) {
if (bool(implementation_data.ss_effects_flags & SCREEN_SPACE_EFFECTS_FLAGS_USE_SSAO)) {
#ifdef USE_MULTIVIEW
- float ssao = texture(sampler2DArray(ao_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(screen_uv, ViewIndex)).r;
+ float ssao = texture(sampler2DArray(ao_buffer, SAMPLER_LINEAR_CLAMP), vec3(screen_uv, ViewIndex)).r;
#else
- float ssao = texture(sampler2D(ao_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), screen_uv).r;
+ float ssao = texture(sampler2D(ao_buffer, SAMPLER_LINEAR_CLAMP), screen_uv).r;
#endif
ao = min(ao, ssao);
ao_light_affect = mix(ao_light_affect, max(ao_light_affect, implementation_data.ssao_light_affect), implementation_data.ssao_ao_affect);
@@ -1500,9 +1504,9 @@ void fragment_shader(in SceneData scene_data) {
if (bool(implementation_data.ss_effects_flags & SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL)) {
#ifdef USE_MULTIVIEW
- vec4 ssil = textureLod(sampler2DArray(ssil_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(screen_uv, ViewIndex), 0.0);
+ vec4 ssil = textureLod(sampler2DArray(ssil_buffer, SAMPLER_LINEAR_CLAMP), vec3(screen_uv, ViewIndex), 0.0);
#else
- vec4 ssil = textureLod(sampler2D(ssil_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), screen_uv, 0.0);
+ vec4 ssil = textureLod(sampler2D(ssil_buffer, SAMPLER_LINEAR_CLAMP), screen_uv, 0.0);
#endif // USE_MULTIVIEW
ambient_light *= 1.0 - ssil.a;
ambient_light += ssil.rgb * albedo.rgb;
@@ -1786,7 +1790,7 @@ void fragment_shader(in SceneData scene_data) {
vec4 trans_coord = directional_lights.data[i].shadow_matrix1 * trans_vertex;
trans_coord /= trans_coord.w;
- float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r;
+ float shadow_z = textureLod(sampler2D(directional_shadow_atlas, SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r;
shadow_z *= directional_lights.data[i].shadow_z_range.x;
float z = trans_coord.z * directional_lights.data[i].shadow_z_range.x;
@@ -1796,7 +1800,7 @@ void fragment_shader(in SceneData scene_data) {
vec4 trans_coord = directional_lights.data[i].shadow_matrix2 * trans_vertex;
trans_coord /= trans_coord.w;
- float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r;
+ float shadow_z = textureLod(sampler2D(directional_shadow_atlas, SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r;
shadow_z *= directional_lights.data[i].shadow_z_range.y;
float z = trans_coord.z * directional_lights.data[i].shadow_z_range.y;
@@ -1806,7 +1810,7 @@ void fragment_shader(in SceneData scene_data) {
vec4 trans_coord = directional_lights.data[i].shadow_matrix3 * trans_vertex;
trans_coord /= trans_coord.w;
- float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r;
+ float shadow_z = textureLod(sampler2D(directional_shadow_atlas, SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r;
shadow_z *= directional_lights.data[i].shadow_z_range.z;
float z = trans_coord.z * directional_lights.data[i].shadow_z_range.z;
@@ -1817,7 +1821,7 @@ void fragment_shader(in SceneData scene_data) {
vec4 trans_coord = directional_lights.data[i].shadow_matrix4 * trans_vertex;
trans_coord /= trans_coord.w;
- float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r;
+ float shadow_z = textureLod(sampler2D(directional_shadow_atlas, SAMPLER_LINEAR_CLAMP), trans_coord.xy, 0.0).r;
shadow_z *= directional_lights.data[i].shadow_z_range.w;
float z = trans_coord.z * directional_lights.data[i].shadow_z_range.w;
@@ -2038,8 +2042,8 @@ void fragment_shader(in SceneData scene_data) {
if (alpha < alpha_scissor) {
discard;
}
-#endif // ALPHA_SCISSOR_USED
-
+#else
+#ifdef MODE_RENDER_DEPTH
#ifdef USE_OPAQUE_PREPASS
if (alpha < scene_data.opaque_prepass_threshold) {
@@ -2047,6 +2051,8 @@ void fragment_shader(in SceneData scene_data) {
}
#endif // USE_OPAQUE_PREPASS
+#endif // MODE_RENDER_DEPTH
+#endif // ALPHA_SCISSOR_USED
#endif // USE_SHADOW_TO_OPACITY
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
index 043bba1e4e..8ead363f3b 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
@@ -42,20 +42,7 @@ draw_call;
#include "../light_data_inc.glsl"
-#define SAMPLER_NEAREST_CLAMP 0
-#define SAMPLER_LINEAR_CLAMP 1
-#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
-#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
-#define SAMPLER_NEAREST_REPEAT 6
-#define SAMPLER_LINEAR_REPEAT 7
-#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
-#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
-
-layout(set = 0, binding = 1) uniform sampler material_samplers[12];
+#include "../samplers_inc.glsl"
layout(set = 0, binding = 2) uniform sampler shadow_sampler;
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
index b43138606f..b63eea1401 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
@@ -624,10 +624,10 @@ vec4 fog_process(vec3 vertex) {
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
float lod, blend;
blend = modf(mip_level * MAX_ROUGHNESS_LOD, lod);
- sky_fog_color = texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(cube_view, lod)).rgb;
- sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(cube_view, lod + 1)).rgb, blend);
+ sky_fog_color = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb;
+ sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend);
#else
- sky_fog_color = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb;
+ sky_fog_color = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective);
}
@@ -817,11 +817,15 @@ void main() {
alpha = compute_alpha_antialiasing_edge(alpha, alpha_texture_coordinate, alpha_antialiasing_edge);
#endif // ALPHA_ANTIALIASING_EDGE_USED
+#ifdef MODE_RENDER_DEPTH
#ifdef USE_OPAQUE_PREPASS
+#ifndef ALPHA_SCISSOR_USED
if (alpha < scene_data.opaque_prepass_threshold) {
discard;
}
+#endif // !ALPHA_SCISSOR_USED
#endif // USE_OPAQUE_PREPASS
+#endif // MODE_RENDER_DEPTH
#endif // !USE_SHADOW_TO_OPACITY
@@ -1007,11 +1011,11 @@ void main() {
float lod, blend;
blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod);
- specular_light = texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ref_vec, lod)).rgb;
- specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ref_vec, lod + 1)).rgb, blend);
+ specular_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
+ specular_light = mix(specular_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
#else // USE_RADIANCE_CUBEMAP_ARRAY
- specular_light = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
+ specular_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
specular_light *= sc_luminance_multiplier;
@@ -1032,9 +1036,9 @@ void main() {
if (scene_data.use_ambient_cubemap) {
vec3 ambient_dir = scene_data.radiance_inverse_xform * normal;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
- vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
+ vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
#else
- vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ambient_dir, MAX_ROUGHNESS_LOD).rgb;
+ vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
cubemap_ambient *= sc_luminance_multiplier;
cubemap_ambient *= scene_data.IBL_exposure_normalization;
@@ -1066,11 +1070,11 @@ void main() {
float lod, blend;
blend = modf(roughness_lod, lod);
- vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ref_vec, lod)).rgb;
- clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(ref_vec, lod + 1)).rgb, blend);
+ vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
+ clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
#else
- vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ref_vec, roughness_lod).rgb;
+ vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
@@ -1117,10 +1121,10 @@ void main() {
if (uses_sh) {
uvw.z *= 4.0; //SH textures use 4 times more data
- vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
- vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
- vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
- vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
+ vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
+ vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
+ vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
+ vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
vec3 n = normalize(lightmaps.data[idx].normal_xform * normal);
float exposure_normalization = lightmaps.data[idx].exposure_normalization;
@@ -1137,7 +1141,7 @@ void main() {
}
} else {
- ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb * lightmaps.data[idx].exposure_normalization;
+ ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[idx].exposure_normalization;
}
}
@@ -1685,8 +1689,8 @@ void main() {
if (alpha < alpha_scissor) {
discard;
}
-#endif // ALPHA_SCISSOR_USED
-
+#else
+#ifdef MODE_RENDER_DEPTH
#ifdef USE_OPAQUE_PREPASS
if (alpha < scene_data.opaque_prepass_threshold) {
@@ -1694,6 +1698,8 @@ void main() {
}
#endif // USE_OPAQUE_PREPASS
+#endif // MODE_RENDER_DEPTH
+#endif // !ALPHA_SCISSOR_USED
#endif // USE_SHADOW_TO_OPACITY
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl
index f16f80c6f6..d0a315858d 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl
@@ -36,20 +36,7 @@ draw_call;
#include "../light_data_inc.glsl"
-#define SAMPLER_NEAREST_CLAMP 0
-#define SAMPLER_LINEAR_CLAMP 1
-#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
-#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
-#define SAMPLER_NEAREST_REPEAT 6
-#define SAMPLER_LINEAR_REPEAT 7
-#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
-#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
-
-layout(set = 0, binding = 1) uniform sampler material_samplers[12];
+#include "../samplers_inc.glsl"
layout(set = 0, binding = 2) uniform sampler shadow_sampler;
diff --git a/servers/rendering/renderer_rd/shaders/particles.glsl b/servers/rendering/renderer_rd/shaders/particles.glsl
index a609076e2c..7ba03a27f7 100644
--- a/servers/rendering/renderer_rd/shaders/particles.glsl
+++ b/servers/rendering/renderer_rd/shaders/particles.glsl
@@ -6,24 +6,11 @@
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
-#define SAMPLER_NEAREST_CLAMP 0
-#define SAMPLER_LINEAR_CLAMP 1
-#define SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP 2
-#define SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP 3
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP 4
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP 5
-#define SAMPLER_NEAREST_REPEAT 6
-#define SAMPLER_LINEAR_REPEAT 7
-#define SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT 8
-#define SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT 9
-#define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10
-#define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11
-
#define SDF_MAX_LENGTH 16384.0
/* SET 0: GLOBAL DATA */
-layout(set = 0, binding = 1) uniform sampler material_samplers[12];
+#include "samplers_inc.glsl"
layout(set = 0, binding = 2, std430) restrict readonly buffer GlobalShaderUniformData {
vec4 data[];
@@ -473,7 +460,7 @@ void main() {
if (any(lessThan(uvw_pos, vec3(0.0))) || any(greaterThan(uvw_pos, vec3(1.0)))) {
continue;
}
- vec3 s = texture(sampler3D(sdf_vec_textures[FRAME.attractors[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).xyz * -2.0 + 1.0;
+ vec3 s = texture(sampler3D(sdf_vec_textures[FRAME.attractors[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos).xyz * -2.0 + 1.0;
dir = mat3(FRAME.attractors[i].transform) * safe_normalize(s); //revert direction
amount = length(s);
@@ -509,15 +496,15 @@ void main() {
vec2 sdf_pos2 = vec2(dot(vec4(pos2, 0, 1), to_sdf_x), dot(vec4(pos2, 0, 1), to_sdf_y));
float sdf_particle_size = distance(sdf_pos, sdf_pos2);
- float d = texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos).r * SDF_MAX_LENGTH;
+ float d = texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uv_pos).r * SDF_MAX_LENGTH;
d -= sdf_particle_size;
if (d < 0.0) {
const float EPSILON = 0.001;
vec2 n = normalize(vec2(
- texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos + vec2(EPSILON, 0.0)).r - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos - vec2(EPSILON, 0.0)).r,
- texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos + vec2(0.0, EPSILON)).r - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos - vec2(0.0, EPSILON)).r));
+ texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uv_pos + vec2(EPSILON, 0.0)).r - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uv_pos - vec2(EPSILON, 0.0)).r,
+ texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uv_pos + vec2(0.0, EPSILON)).r - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uv_pos - vec2(0.0, EPSILON)).r));
collided = true;
sdf_pos2 = sdf_pos + n * d;
@@ -596,7 +583,7 @@ void main() {
}
vec3 uvw_pos = (local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5;
- float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).r;
+ float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos).r;
s *= FRAME.colliders[i].scale;
s += extra_dist;
if (s < particle_size) {
@@ -606,9 +593,9 @@ void main() {
normal = mat3(FRAME.colliders[i].transform) *
normalize(
vec3(
- texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r,
- texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, EPSILON, 0.0)).r,
- texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, 0.0, EPSILON)).r));
+ texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r,
+ texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos - vec3(0.0, EPSILON, 0.0)).r,
+ texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], SAMPLER_LINEAR_CLAMP), uvw_pos - vec3(0.0, 0.0, EPSILON)).r));
}
} break;
@@ -623,14 +610,14 @@ void main() {
vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5;
- float y = 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz).r;
+ float y = 1.0 - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz).r;
if (y > uvw_pos.y) {
//inside heightfield
vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
- vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
- vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+ vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+ vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents;
normal = normalize(cross(pos1 - pos2, pos1 - pos3));
float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y;
diff --git a/servers/rendering/renderer_rd/shaders/samplers_inc.glsl b/servers/rendering/renderer_rd/shaders/samplers_inc.glsl
new file mode 100644
index 0000000000..3f6172795e
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/samplers_inc.glsl
@@ -0,0 +1,12 @@
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 0) uniform sampler SAMPLER_NEAREST_CLAMP;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 1) uniform sampler SAMPLER_LINEAR_CLAMP;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 2) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 3) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 4) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 5) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 6) uniform sampler SAMPLER_NEAREST_REPEAT;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 7) uniform sampler SAMPLER_LINEAR_REPEAT;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 8) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 9) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 10) uniform sampler SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT;
+layout(set = 0, binding = SAMPLERS_BINDING_FIRST_INDEX + 11) uniform sampler SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT; \ No newline at end of file
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl
index ae5e1b7251..8f68030cba 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl
@@ -13,7 +13,7 @@ vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction,
if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + half_diameter * cell_size)))) {
break;
}
- vec4 scolor = textureLod(sampler3D(probe, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2(diameter));
+ vec4 scolor = textureLod(sampler3D(probe, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, log2(diameter));
float a = (1.0 - color.a);
color += a * scolor;
dist += half_diameter;
@@ -35,7 +35,7 @@ vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3
if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + radius * cell_size)))) {
break;
}
- vec4 scolor = textureLod(sampler3D(probe, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level);
+ vec4 scolor = textureLod(sampler3D(probe, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), uvw_pos, lod_level);
lod_level += 1.0;
float a = (1.0 - color.a);
@@ -176,7 +176,7 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal
}
occ_pos *= sdfgi.occlusion_renormalize;
- float occlusion = dot(textureLod(sampler3D(sdfgi_occlusion_cascades, material_samplers[SAMPLER_LINEAR_CLAMP]), occ_pos, 0.0), occ_mask);
+ float occlusion = dot(textureLod(sampler3D(sdfgi_occlusion_cascades, SAMPLER_LINEAR_CLAMP), occ_pos, 0.0), occ_mask);
weight *= max(occlusion, 0.01);
}
@@ -187,7 +187,7 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal
vec3 pos_uvw = diffuse_posf;
pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy;
pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z;
- diffuse = textureLod(sampler2DArray(sdfgi_lightprobe_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw, 0.0).rgb;
+ diffuse = textureLod(sampler2DArray(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw, 0.0).rgb;
diffuse_accum += vec4(diffuse * weight * sdfgi.cascades[cascade].exposure_normalization, weight);
@@ -197,10 +197,10 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal
pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy;
pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z;
if (roughness < 0.99) {
- specular = textureLod(sampler2DArray(sdfgi_lightprobe_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb;
+ specular = textureLod(sampler2DArray(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb;
}
if (roughness > 0.5) {
- specular = mix(specular, textureLod(sampler2DArray(sdfgi_lightprobe_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw, 0.0).rgb, (roughness - 0.5) * 2.0);
+ specular = mix(specular, textureLod(sampler2DArray(sdfgi_lightprobe_texture, SAMPLER_LINEAR_CLAMP), pos_uvw, 0.0).rgb, (roughness - 0.5) * 2.0);
}
specular_accum += specular * weight * sdfgi.cascades[cascade].exposure_normalization;
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
index e159e5628c..ba8d901772 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -374,7 +374,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex
for (uint i = 0; i < sc_directional_penumbra_shadow_samples; i++) {
vec2 suv = pssm_coord.xy + (disk_rotation * scene_data_block.data.directional_penumbra_shadow_kernel[i].xy) * tex_scale;
- float d = textureLod(sampler2D(shadow, material_samplers[SAMPLER_LINEAR_CLAMP]), suv, 0.0).r;
+ float d = textureLod(sampler2D(shadow, SAMPLER_LINEAR_CLAMP), suv, 0.0).r;
if (d < pssm_coord.z) {
blocker_average += d;
blocker_count += 1.0;
@@ -478,7 +478,7 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
pos.xy = pos.xy * 0.5 + 0.5;
pos.xy = uv_rect.xy + pos.xy * uv_rect.zw;
- float d = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), pos.xy, 0.0).r;
+ float d = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), pos.xy, 0.0).r;
if (d < z_norm) {
blocker_average += d;
blocker_count += 1.0;
@@ -605,7 +605,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
// splane.xy = clamp(splane.xy,clamp_rect.xy + scene_data_block.data.shadow_atlas_pixel_size,clamp_rect.xy + clamp_rect.zw - scene_data_block.data.shadow_atlas_pixel_size );
splane.w = 1.0; //needed? i think it should be 1 already
- float shadow_z = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), splane.xy, 0.0).r;
+ float shadow_z = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), splane.xy, 0.0).r;
transmittance_z = (splane.z - shadow_z) / omni_lights.data[idx].inv_radius;
}
#endif
@@ -734,7 +734,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
for (uint i = 0; i < sc_penumbra_shadow_samples; i++) {
vec2 suv = shadow_uv + (disk_rotation * scene_data_block.data.penumbra_shadow_kernel[i].xy) * uv_size;
suv = clamp(suv, spot_lights.data[idx].atlas_rect.xy, clamp_max);
- float d = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), suv, 0.0).r;
+ float d = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), suv, 0.0).r;
if (d < splane.z) {
blocker_average += d;
blocker_count += 1.0;
@@ -838,7 +838,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
splane /= splane.w;
splane.xy = splane.xy * spot_lights.data[idx].atlas_rect.zw + spot_lights.data[idx].atlas_rect.xy;
- float shadow_z = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), splane.xy, 0.0).r;
+ float shadow_z = textureLod(sampler2D(shadow_atlas, SAMPLER_LINEAR_CLAMP), splane.xy, 0.0).r;
shadow_z = shadow_z * 2.0 - 1.0;
float z_far = 1.0 / spot_lights.data[idx].inv_radius;
@@ -932,7 +932,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal,
vec4 reflection;
- reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier;
+ reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier;
reflection.rgb *= reflections.data[ref_index].exposure_normalization;
if (reflections.data[ref_index].exterior) {
reflection.rgb = mix(specular_light, reflection.rgb, blend);
@@ -955,7 +955,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal,
vec4 ambient_out;
- ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb;
+ ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb;
ambient_out.rgb *= reflections.data[ref_index].exposure_normalization;
ambient_out.a = blend;
if (reflections.data[ref_index].exterior) {
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index 8d1eb87e3c..d055f01009 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -2398,6 +2398,26 @@ void MaterialStorage::material_update_dependency(RID p_material, DependencyTrack
}
}
+Vector<RD::Uniform> MaterialStorage::get_default_sampler_uniforms(int first_index) {
+ Vector<RD::Uniform> uniforms;
+
+ // Binding ids are aligned with samplers_inc.glsl.
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 0, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 1, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 2, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 3, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 4, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 5, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 6, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 7, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 8, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 9, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 10, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED)));
+ uniforms.push_back(RD::Uniform(RD::UNIFORM_TYPE_SAMPLER, first_index + 11, sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED)));
+
+ return uniforms;
+}
+
void MaterialStorage::material_set_data_request_function(ShaderType p_shader_type, MaterialStorage::MaterialDataRequestFunction p_function) {
ERR_FAIL_INDEX(p_shader_type, SHADER_TYPE_MAX);
material_data_request_func[p_shader_type] = p_function;
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h
index ac217d9a49..b6da3df783 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h
@@ -344,6 +344,8 @@ public:
void sampler_rd_configure_custom(float mipmap_bias);
+ Vector<RD::Uniform> get_default_sampler_uniforms(int first_index);
+
// void sampler_rd_set_default(float p_mipmap_bias);
/* Buffers */
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index 1500c902c6..8f647be5c9 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -54,10 +54,11 @@ ParticlesStorage::ParticlesStorage() {
/* Particles */
{
+ String defines = "#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n";
// Initialize particles
Vector<String> particles_modes;
particles_modes.push_back("");
- particles_shader.shader.initialize(particles_modes, String());
+ particles_shader.shader.initialize(particles_modes, defines);
}
MaterialStorage::get_singleton()->shader_set_data_request_function(MaterialStorage::SHADER_TYPE_PARTICLES, _create_particles_shader_funcs);
MaterialStorage::get_singleton()->material_set_data_request_function(MaterialStorage::SHADER_TYPE_PARTICLES, _create_particles_material_funcs);
@@ -109,7 +110,6 @@ ParticlesStorage::ParticlesStorage() {
actions.render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n";
actions.render_mode_defines["collision_use_scale"] = "#define USE_COLLISION_SCALE\n";
- actions.sampler_array_name = "material_samplers";
actions.base_texture_binding_index = 1;
actions.texture_layout_set = 3;
actions.base_uniform_string = "material.";
@@ -145,27 +145,6 @@ void process() {
Vector<RD::Uniform> uniforms;
{
- Vector<RID> ids;
- ids.resize(12);
- RID *ids_ptr = ids.ptrw();
- ids_ptr[0] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[1] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[2] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[3] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[4] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[5] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
- ids_ptr[6] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[7] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[8] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[9] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[10] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
- ids_ptr[11] = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
-
- RD::Uniform u(RD::UNIFORM_TYPE_SAMPLER, 1, ids);
- uniforms.push_back(u);
- }
-
- {
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 2;
@@ -173,7 +152,9 @@ void process() {
uniforms.push_back(u);
}
- particles_shader.base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 0);
+ uniforms.append_array(material_storage->get_default_sampler_uniforms(SAMPLERS_BINDING_FIRST_INDEX));
+
+ particles_shader.base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, BASE_UNIFORM_SET);
}
{
@@ -430,7 +411,7 @@ void ParticlesStorage::particles_set_fractional_delta(RID p_particles, bool p_en
void ParticlesStorage::particles_set_trails(RID p_particles, bool p_enable, double p_length) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_COND(!particles);
- ERR_FAIL_COND(p_length < 0.1);
+ ERR_FAIL_COND(p_length < 0.01);
p_length = MIN(10.0, p_length);
particles->trails_enabled = p_enable;
@@ -1119,9 +1100,9 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
//todo should maybe compute all particle systems together?
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, m->shader_data->pipeline);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles_shader.base_uniform_set, 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->particles_material_uniform_set, 1);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->collision_textures_uniform_set, 2);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles_shader.base_uniform_set, BASE_UNIFORM_SET);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->particles_material_uniform_set, MATERIAL_UNIFORM_SET);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->collision_textures_uniform_set, COLLISION_TEXTURTES_UNIFORM_SET);
if (m->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(m->uniform_set)) {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, m->uniform_set, 3);
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h
index 4f9253144e..612420a36c 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h
@@ -53,6 +53,14 @@ private:
/* PARTICLES */
+ enum {
+ BASE_UNIFORM_SET,
+ MATERIAL_UNIFORM_SET,
+ COLLISION_TEXTURTES_UNIFORM_SET,
+ };
+
+ const int SAMPLERS_BINDING_FIRST_INDEX = 3;
+
struct ParticleData {
float xform[16];
float velocity[3];
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
index 609fb2afe7..71bc21d5d4 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp
@@ -44,15 +44,28 @@ RenderSceneBuffersRD::~RenderSceneBuffersRD() {
void RenderSceneBuffersRD::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_texture", "context", "name"), &RenderSceneBuffersRD::has_texture);
- // FIXME we can't pass RD::DataFormat, RD::TextureSamples and RD::TextureView in ClassDB, need to solve views differently...
- // ClassDB::bind_method(D_METHOD("create_texture", "context", "name", "data_format", "usage_bits", "texture_samples", "size", "layers", "mipmaps", "unique"), &RenderSceneBuffersRD::create_texture);
- // ClassDB::bind_method(D_METHOD("create_texture_from_format", "context", "name", "format", "view", "unique"), &RenderSceneBuffersRD::create_texture_from_format);
- // ClassDB::bind_method(D_METHOD("create_texture_view", "context", "name", "view_name", "view"), &RenderSceneBuffersRD::has_texture);
+ ClassDB::bind_method(D_METHOD("create_texture", "context", "name", "data_format", "usage_bits", "texture_samples", "size", "layers", "mipmaps", "unique"), &RenderSceneBuffersRD::create_texture);
+ ClassDB::bind_method(D_METHOD("create_texture_from_format", "context", "name", "format", "view", "unique"), &RenderSceneBuffersRD::_create_texture_from_format);
+ ClassDB::bind_method(D_METHOD("create_texture_view", "context", "name", "view_name", "view"), &RenderSceneBuffersRD::_create_texture_view);
ClassDB::bind_method(D_METHOD("get_texture", "context", "name"), &RenderSceneBuffersRD::get_texture);
- // ClassDB::bind_method(D_METHOD("get_texture_format", "context", "name"), &RenderSceneBuffersRD::get_texture_format);
+ ClassDB::bind_method(D_METHOD("get_texture_format", "context", "name"), &RenderSceneBuffersRD::_get_texture_format);
ClassDB::bind_method(D_METHOD("get_texture_slice", "context", "name", "layer", "mipmap", "layers", "mipmaps"), &RenderSceneBuffersRD::get_texture_slice);
ClassDB::bind_method(D_METHOD("get_texture_slice_size", "context", "name", "mipmap"), &RenderSceneBuffersRD::get_texture_slice_size);
ClassDB::bind_method(D_METHOD("clear_context", "context"), &RenderSceneBuffersRD::clear_context);
+
+ // Access to some core buffers so users don't need to know their names.
+ ClassDB::bind_method(D_METHOD("get_color_texture"), &RenderSceneBuffersRD::_get_color_texture);
+ ClassDB::bind_method(D_METHOD("get_color_layer", "layer"), &RenderSceneBuffersRD::_get_color_layer);
+ ClassDB::bind_method(D_METHOD("get_depth_texture"), &RenderSceneBuffersRD::_get_depth_texture);
+ ClassDB::bind_method(D_METHOD("get_depth_layer", "layer"), &RenderSceneBuffersRD::_get_depth_layer);
+ ClassDB::bind_method(D_METHOD("get_velocity_texture"), &RenderSceneBuffersRD::_get_velocity_texture);
+ ClassDB::bind_method(D_METHOD("get_velocity_layer", "layer"), &RenderSceneBuffersRD::_get_velocity_layer);
+
+ // Expose a few properties we're likely to use externally
+ ClassDB::bind_method(D_METHOD("get_render_target"), &RenderSceneBuffersRD::get_render_target);
+ ClassDB::bind_method(D_METHOD("get_view_count"), &RenderSceneBuffersRD::get_view_count);
+ ClassDB::bind_method(D_METHOD("get_internal_size"), &RenderSceneBuffersRD::get_internal_size);
+ ClassDB::bind_method(D_METHOD("get_use_taa"), &RenderSceneBuffersRD::get_use_taa);
}
void RenderSceneBuffersRD::update_sizes(NamedTexture &p_named_texture) {
@@ -90,42 +103,44 @@ void RenderSceneBuffersRD::cleanup() {
named_textures.clear();
}
-void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
+void RenderSceneBuffersRD::configure(const RenderSceneBuffersConfiguration *p_config) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
- ERR_FAIL_COND_MSG(p_view_count == 0, "Must have at least 1 view");
+ render_target = p_config->get_render_target();
+ target_size = p_config->get_target_size();
+ internal_size = p_config->get_internal_size();
+ view_count = p_config->get_view_count();
+
+ scaling_3d_mode = p_config->get_scaling_3d_mode();
+ msaa_3d = p_config->get_msaa_3d();
+ screen_space_aa = p_config->get_screen_space_aa();
- target_size = p_target_size;
- internal_size = p_internal_size;
- scaling_3d_mode = p_scaling_3d_mode;
+ fsr_sharpness = p_config->get_fsr_sharpness();
+ texture_mipmap_bias = p_config->get_texture_mipmap_bias();
+ use_taa = p_config->get_use_taa();
+ use_debanding = p_config->get_use_debanding();
- if (p_use_taa) {
+ ERR_FAIL_COND_MSG(view_count == 0, "Must have at least 1 view");
+
+ if (use_taa) {
// Use negative mipmap LOD bias when TAA is enabled to compensate for loss of sharpness.
// This restores sharpness in still images to be roughly at the same level as without TAA,
// but moving scenes will still be blurrier.
- p_texture_mipmap_bias -= 0.5;
+ texture_mipmap_bias -= 0.5;
}
- if (p_screen_space_aa == RS::VIEWPORT_SCREEN_SPACE_AA_FXAA) {
+ if (screen_space_aa == RS::VIEWPORT_SCREEN_SPACE_AA_FXAA) {
// Use negative mipmap LOD bias when FXAA is enabled to compensate for loss of sharpness.
// If both TAA and FXAA are enabled, combine their negative LOD biases together.
- p_texture_mipmap_bias -= 0.25;
+ texture_mipmap_bias -= 0.25;
}
- material_storage->sampler_rd_configure_custom(p_texture_mipmap_bias);
+ material_storage->sampler_rd_configure_custom(texture_mipmap_bias);
// need to check if we really need to do this here..
RendererSceneRenderRD::get_singleton()->update_uniform_sets();
- render_target = p_render_target;
- fsr_sharpness = p_fsr_sharpness;
- msaa_3d = p_msaa_3d;
- screen_space_aa = p_screen_space_aa;
- use_taa = p_use_taa;
- use_debanding = p_use_debanding;
- view_count = p_view_count;
-
// cleanout any old buffers we had.
cleanup();
@@ -161,9 +176,33 @@ void RenderSceneBuffersRD::configure(RID p_render_target, const Size2i p_interna
create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH, format, usage_bits);
}
+ // Create our MSAA buffers
+ if (msaa_3d == RS::VIEWPORT_MSAA_DISABLED) {
+ texture_samples = RD::TEXTURE_SAMPLES_1;
+ } else {
+ RD::DataFormat format = base_data_format;
+ uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
+
+ const RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = {
+ RD::TEXTURE_SAMPLES_1,
+ RD::TEXTURE_SAMPLES_2,
+ RD::TEXTURE_SAMPLES_4,
+ RD::TEXTURE_SAMPLES_8,
+ };
+
+ texture_samples = ts[msaa_3d];
+
+ create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA, format, usage_bits, texture_samples);
+
+ usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
+ format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, usage_bits) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT;
+
+ create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA, format, usage_bits, texture_samples);
+ }
+
// VRS (note, our vrs object will only be set if VRS is supported)
RID vrs_texture;
- RS::ViewportVRSMode vrs_mode = texture_storage->render_target_get_vrs_mode(p_render_target);
+ RS::ViewportVRSMode vrs_mode = texture_storage->render_target_get_vrs_mode(render_target);
if (vrs && vrs_mode != RS::VIEWPORT_VRS_DISABLED) {
uint32_t usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
vrs_texture = create_texture(RB_SCOPE_VRS, RB_TEXTURE, RD::DATA_FORMAT_R8_UINT, usage_bits, RD::TEXTURE_SAMPLES_1, vrs->get_vrs_texture_size(internal_size));
@@ -244,6 +283,17 @@ RID RenderSceneBuffersRD::create_texture(const StringName &p_context, const Stri
return create_texture_from_format(p_context, p_texture_name, tf, RD::TextureView(), p_unique);
}
+RID RenderSceneBuffersRD::_create_texture_from_format(const StringName &p_context, const StringName &p_texture_name, const Ref<RDTextureFormat> &p_texture_format, const Ref<RDTextureView> &p_view, bool p_unique) {
+ ERR_FAIL_COND_V(p_texture_format.is_null(), RID());
+
+ RD::TextureView texture_view;
+ if (p_view.is_valid()) { // only use when supplied, else default.
+ texture_view = p_view->base;
+ }
+
+ return create_texture_from_format(p_context, p_texture_name, p_texture_format->base, texture_view, p_unique);
+}
+
RID RenderSceneBuffersRD::create_texture_from_format(const StringName &p_context, const StringName &p_texture_name, const RD::TextureFormat &p_texture_format, RD::TextureView p_view, bool p_unique) {
// TODO p_unique, if p_unique is true, this is a texture that can be shared. This will be implemented later as an optimization.
@@ -272,6 +322,15 @@ RID RenderSceneBuffersRD::create_texture_from_format(const StringName &p_context
return named_texture.texture;
}
+RID RenderSceneBuffersRD::_create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName p_view_name, const Ref<RDTextureView> p_view) {
+ RD::TextureView texture_view;
+ if (p_view.is_valid()) { // only use when supplied, else default.
+ texture_view = p_view->base;
+ }
+
+ return create_texture_view(p_context, p_texture_name, p_view_name, texture_view);
+}
+
RID RenderSceneBuffersRD::create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName p_view_name, RD::TextureView p_view) {
NTKey view_key(p_context, p_view_name);
@@ -310,6 +369,15 @@ RID RenderSceneBuffersRD::get_texture(const StringName &p_context, const StringN
return named_textures[key].texture;
}
+Ref<RDTextureFormat> RenderSceneBuffersRD::_get_texture_format(const StringName &p_context, const StringName &p_texture_name) const {
+ Ref<RDTextureFormat> tf;
+ tf.instantiate();
+
+ tf->base = get_texture_format(p_context, p_texture_name);
+
+ return tf;
+}
+
const RD::TextureFormat RenderSceneBuffersRD::get_texture_format(const StringName &p_context, const StringName &p_texture_name) const {
NTKey key(p_context, p_texture_name);
@@ -535,15 +603,6 @@ void RenderSceneBuffersRD::ensure_velocity() {
uint32_t msaa_usage_bits = usage_bits | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
- const RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = {
- RD::TEXTURE_SAMPLES_1,
- RD::TEXTURE_SAMPLES_2,
- RD::TEXTURE_SAMPLES_4,
- RD::TEXTURE_SAMPLES_8,
- };
-
- RD::TextureSamples texture_samples = ts[msaa_3d];
-
create_texture(RB_SCOPE_BUFFERS, RB_TEX_VELOCITY_MSAA, RD::DATA_FORMAT_R16G16_SFLOAT, msaa_usage_bits, texture_samples);
}
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
index 9a299a3415..85140c674c 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h
@@ -36,6 +36,7 @@
#include "core/templates/hash_map.h"
#include "render_buffer_custom_data_rd.h"
#include "servers/rendering/rendering_device.h"
+#include "servers/rendering/rendering_device_binds.h"
#include "servers/rendering/rendering_method.h"
#include "servers/rendering/storage/render_scene_buffers.h"
@@ -75,12 +76,14 @@ private:
Size2i internal_size = Size2i(0, 0);
RS::ViewportScaling3DMode scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
float fsr_sharpness = 0.2f;
+ float texture_mipmap_bias = 0.0f;
// Aliassing settings
RS::ViewportMSAA msaa_3d = RS::VIEWPORT_MSAA_DISABLED;
RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED;
bool use_taa = false;
bool use_debanding = false;
+ RD::TextureSamples texture_samples = RD::TEXTURE_SAMPLES_1;
// Named Textures
@@ -166,7 +169,7 @@ public:
void set_vrs(RendererRD::VRS *p_vrs) { vrs = p_vrs; }
void cleanup();
- virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override;
+ virtual void configure(const RenderSceneBuffersConfiguration *p_config) override;
void configure_for_reflections(const Size2i p_reflection_size);
virtual void set_fsr_sharpness(float p_fsr_sharpness) override;
virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override;
@@ -202,6 +205,7 @@ public:
_FORCE_INLINE_ RS::ViewportScaling3DMode get_scaling_3d_mode() const { return scaling_3d_mode; }
_FORCE_INLINE_ float get_fsr_sharpness() const { return fsr_sharpness; }
_FORCE_INLINE_ RS::ViewportMSAA get_msaa_3d() const { return msaa_3d; }
+ _FORCE_INLINE_ RD::TextureSamples get_texture_samples() const { return texture_samples; }
_FORCE_INLINE_ RS::ViewportScreenSpaceAA get_screen_space_aa() const { return screen_space_aa; }
_FORCE_INLINE_ bool get_use_taa() const { return use_taa; }
_FORCE_INLINE_ bool get_use_debanding() const { return use_debanding; }
@@ -220,11 +224,24 @@ public:
_FORCE_INLINE_ RID get_internal_texture(const uint32_t p_layer) {
return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_COLOR, p_layer, 0);
}
+ _FORCE_INLINE_ RID get_color_msaa() const {
+ return get_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA);
+ }
+ _FORCE_INLINE_ RID get_color_msaa(uint32_t p_layer) {
+ return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA, p_layer, 0);
+ }
bool has_depth_texture();
RID get_depth_texture();
RID get_depth_texture(const uint32_t p_layer);
+ RID get_depth_msaa() const {
+ return get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA);
+ }
+ RID get_depth_msaa(uint32_t p_layer) {
+ return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA, p_layer, 0);
+ }
+
// back buffer (color)
RID get_back_buffer_texture() const { return has_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0) ? get_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0) : RID(); } // We (re)use our blur texture here.
@@ -236,8 +253,78 @@ public:
RID get_velocity_buffer(bool p_get_msaa, uint32_t p_layer);
////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // Everything after this needs to be re-evaluated, this is all old implementation
+ // Our classDB doesn't support calling our normal exposed functions
+
+private:
+ RID _create_texture_from_format(const StringName &p_context, const StringName &p_texture_name, const Ref<RDTextureFormat> &p_texture_format, const Ref<RDTextureView> &p_view = Ref<RDTextureView>(), bool p_unique = true);
+ RID _create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName p_view_name, const Ref<RDTextureView> p_view = Ref<RDTextureView>());
+ Ref<RDTextureFormat> _get_texture_format(const StringName &p_context, const StringName &p_texture_name) const;
+
+ // For color and depth as exposed to extensions, we return the buffer that we're rendering into.
+ // Resolving happens after effects etc. are run.
+ RID _get_color_texture() {
+ if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED && has_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA)) {
+ return get_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA);
+ } else if (has_internal_texture()) {
+ return get_internal_texture();
+ } else {
+ return RID();
+ }
+ }
+
+ RID _get_color_layer(const uint32_t p_layer) {
+ if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED && has_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA)) {
+ return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA, p_layer, 0);
+ } else if (has_internal_texture()) {
+ return get_internal_texture(p_layer);
+ } else {
+ return RID();
+ }
+ }
+
+ RID _get_depth_texture() {
+ if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED && has_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA)) {
+ return get_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA);
+ } else if (has_depth_texture()) {
+ return get_depth_texture();
+ } else {
+ return RID();
+ }
+ }
+
+ RID _get_depth_layer(const uint32_t p_layer) {
+ if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED && has_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA)) {
+ return get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA, p_layer, 0);
+ } else if (has_depth_texture()) {
+ return get_depth_texture(p_layer);
+ } else {
+ return RID();
+ }
+ }
+
+ RID _get_velocity_texture() {
+ if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED && has_velocity_buffer(true)) {
+ return get_velocity_buffer(true);
+ } else if (has_velocity_buffer(false)) {
+ return get_velocity_buffer(false);
+ } else {
+ return RID();
+ }
+ }
+
+ RID _get_velocity_layer(const uint32_t p_layer) {
+ if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED && has_velocity_buffer(true)) {
+ return get_velocity_buffer(true, p_layer);
+ } else if (has_velocity_buffer(false)) {
+ return get_velocity_buffer(false, p_layer);
+ } else {
+ return RID();
+ }
+ }
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Everything after this needs to be re-evaluated, this is all old implementation
+public:
struct WeightBuffers {
RID weight;
RID fb; // FB with both texture and weight writing into one level lower
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index 3c4e792b37..d84f6e6850 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -1395,6 +1395,13 @@ String TextureStorage::texture_get_path(RID p_texture) const {
return tex->path;
}
+Image::Format TextureStorage::texture_get_format(RID p_texture) const {
+ Texture *tex = texture_owner.get_or_null(p_texture);
+ ERR_FAIL_COND_V(!tex, Image::FORMAT_MAX);
+
+ return tex->format;
+}
+
void TextureStorage::texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) {
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_COND(!tex);
@@ -1429,6 +1436,79 @@ Size2 TextureStorage::texture_size_with_proxy(RID p_proxy) {
return texture_2d_get_size(p_proxy);
}
+void TextureStorage::texture_rd_initialize(RID p_texture, const RID &p_rd_texture, const RS::TextureLayeredType p_layer_type) {
+ ERR_FAIL_COND(!RD::get_singleton()->texture_is_valid(p_rd_texture));
+
+ // TODO : investigate if we can support this, will need to be able to obtain the order and obtain the slice info
+ ERR_FAIL_COND_MSG(RD::get_singleton()->texture_is_shared(p_rd_texture), "Please create the texture object using the original texture");
+
+ RD::TextureFormat tf = RD::get_singleton()->texture_get_format(p_rd_texture);
+ ERR_FAIL_COND(!(tf.usage_bits & RD::TEXTURE_USAGE_SAMPLING_BIT));
+
+ TextureFromRDFormat imfmt;
+ _texture_format_from_rd(tf.format, imfmt);
+ ERR_FAIL_COND(imfmt.image_format == Image::FORMAT_MAX);
+
+ Texture texture;
+
+ switch (tf.texture_type) {
+ case RD::TEXTURE_TYPE_2D: {
+ ERR_FAIL_COND(tf.array_layers != 1);
+ texture.type = TextureStorage::TYPE_2D;
+ } break;
+ case RD::TEXTURE_TYPE_2D_ARRAY: {
+ // RenderingDevice doesn't distinguish between Array textures and Cube textures
+ // this condition covers TextureArrays, TextureCube, and TextureCubeArray.
+ ERR_FAIL_COND(tf.array_layers == 1);
+ texture.type = TextureStorage::TYPE_LAYERED;
+ texture.layered_type = p_layer_type;
+ } break;
+ case RD::TEXTURE_TYPE_3D: {
+ ERR_FAIL_COND(tf.array_layers != 1);
+ texture.type = TextureStorage::TYPE_3D;
+ } break;
+ default: {
+ ERR_FAIL_MSG("This RD texture can't be used as a render texture");
+ } break;
+ }
+
+ texture.width = tf.width;
+ texture.height = tf.height;
+ texture.depth = tf.depth;
+ texture.layers = tf.array_layers;
+ texture.mipmaps = tf.mipmaps;
+ texture.format = imfmt.image_format;
+ texture.validated_format = texture.format; // ??
+
+ RD::TextureView rd_view;
+ rd_view.format_override = imfmt.rd_format == tf.format ? RD::DATA_FORMAT_MAX : imfmt.rd_format;
+ rd_view.swizzle_r = imfmt.swizzle_r;
+ rd_view.swizzle_g = imfmt.swizzle_g;
+ rd_view.swizzle_b = imfmt.swizzle_b;
+ rd_view.swizzle_a = imfmt.swizzle_a;
+
+ texture.rd_type = tf.texture_type;
+ texture.rd_view = rd_view;
+ texture.rd_format = imfmt.rd_format;
+ // We create a shared texture here even if our view matches, so we don't obtain ownership.
+ texture.rd_texture = RD::get_singleton()->texture_create_shared(rd_view, p_rd_texture);
+ if (imfmt.rd_format_srgb != RD::DATA_FORMAT_MAX) {
+ rd_view.format_override = imfmt.rd_format_srgb == tf.format ? RD::DATA_FORMAT_MAX : imfmt.rd_format;
+ texture.rd_format_srgb = imfmt.rd_format_srgb;
+ // We create a shared texture here even if our view matches, so we don't obtain ownership.
+ texture.rd_texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, p_rd_texture);
+ }
+
+ // TODO figure out what to do with slices
+
+ texture.width_2d = texture.width;
+ texture.height_2d = texture.height;
+ texture.is_render_target = false;
+ texture.is_proxy = false;
+
+ texture_owner.initialize_rid(p_texture, texture);
+}
+
RID TextureStorage::texture_get_rd_texture(RID p_texture, bool p_srgb) const {
if (p_texture.is_null()) {
return RID();
@@ -1921,6 +2001,362 @@ Ref<Image> TextureStorage::_validate_texture_format(const Ref<Image> &p_image, T
return image;
}
+void TextureStorage::_texture_format_from_rd(RD::DataFormat p_rd_format, TextureFromRDFormat &r_format) {
+ switch (p_rd_format) {
+ case RD::DATA_FORMAT_R8_UNORM: {
+ r_format.image_format = Image::FORMAT_L8;
+ r_format.rd_format = RD::DATA_FORMAT_R8_UNORM;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break; //luminance
+ case RD::DATA_FORMAT_R8G8_UNORM: {
+ r_format.image_format = Image::FORMAT_LA8;
+ r_format.rd_format = RD::DATA_FORMAT_R8G8_UNORM;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_G;
+ } break; //luminance-alpha
+ /* already maps to L8/LA8
+ case RD::DATA_FORMAT_R8_UNORM: {
+ r_format.image_format = Image::FORMAT_R8;
+ r_format.rd_format = RD::DATA_FORMAT_R8_UNORM;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break;
+ case RD::DATA_FORMAT_R8G8_UNORM: {
+ r_format.image_format = Image::FORMAT_RG8;
+ r_format.rd_format = RD::DATA_FORMAT_R8G8_UNORM;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break;
+ */
+ case RD::DATA_FORMAT_R8G8B8_UNORM:
+ case RD::DATA_FORMAT_R8G8B8_SRGB: {
+ r_format.image_format = Image::FORMAT_RGB8;
+ r_format.rd_format = RD::DATA_FORMAT_R8G8B8_UNORM;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_R8G8B8_SRGB;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+
+ } break;
+ case RD::DATA_FORMAT_R8G8B8A8_UNORM:
+ case RD::DATA_FORMAT_R8G8B8A8_SRGB: {
+ r_format.image_format = Image::FORMAT_RGBA8;
+ r_format.rd_format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+ } break;
+ case RD::DATA_FORMAT_B4G4R4A4_UNORM_PACK16: {
+ r_format.image_format = Image::FORMAT_RGBA4444;
+ r_format.rd_format = RD::DATA_FORMAT_B4G4R4A4_UNORM_PACK16;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_B; //needs swizzle
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+ } break;
+ case RD::DATA_FORMAT_B5G6R5_UNORM_PACK16: {
+ r_format.image_format = Image::FORMAT_RGB565;
+ r_format.rd_format = RD::DATA_FORMAT_B5G6R5_UNORM_PACK16;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+ } break;
+ case RD::DATA_FORMAT_R32_SFLOAT: {
+ r_format.image_format = Image::FORMAT_RF;
+ r_format.rd_format = RD::DATA_FORMAT_R32_SFLOAT;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break; //float
+ case RD::DATA_FORMAT_R32G32_SFLOAT: {
+ r_format.image_format = Image::FORMAT_RGF;
+ r_format.rd_format = RD::DATA_FORMAT_R32G32_SFLOAT;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break;
+ case RD::DATA_FORMAT_R32G32B32_SFLOAT: {
+ r_format.image_format = Image::FORMAT_RGBF;
+ r_format.rd_format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break;
+ case RD::DATA_FORMAT_R32G32B32A32_SFLOAT: {
+ r_format.image_format = Image::FORMAT_RGBF;
+ r_format.rd_format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+
+ } break;
+ case RD::DATA_FORMAT_R16_SFLOAT: {
+ r_format.image_format = Image::FORMAT_RH;
+ r_format.rd_format = RD::DATA_FORMAT_R16_SFLOAT;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+
+ } break; //half float
+ case RD::DATA_FORMAT_R16G16_SFLOAT: {
+ r_format.image_format = Image::FORMAT_RGH;
+ r_format.rd_format = RD::DATA_FORMAT_R16G16_SFLOAT;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+
+ } break;
+ case RD::DATA_FORMAT_R16G16B16_SFLOAT: {
+ r_format.image_format = Image::FORMAT_RGBH;
+ r_format.rd_format = RD::DATA_FORMAT_R16G16B16_SFLOAT;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break;
+ case RD::DATA_FORMAT_R16G16B16A16_SFLOAT: {
+ r_format.image_format = Image::FORMAT_RGBAH;
+ r_format.rd_format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+
+ } break;
+ case RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32: {
+ r_format.image_format = Image::FORMAT_RGBE9995;
+ r_format.rd_format = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32;
+ // TODO: Need to make a function in Image to swap bits for this.
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_IDENTITY;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_IDENTITY;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_IDENTITY;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_IDENTITY;
+ } break;
+ case RD::DATA_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case RD::DATA_FORMAT_BC1_RGB_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_DXT1;
+ r_format.rd_format = RD::DATA_FORMAT_BC1_RGB_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_BC1_RGB_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+
+ } break; //s3tc bc1
+ case RD::DATA_FORMAT_BC2_UNORM_BLOCK:
+ case RD::DATA_FORMAT_BC2_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_DXT3;
+ r_format.rd_format = RD::DATA_FORMAT_BC2_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_BC2_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+
+ } break; //bc2
+ case RD::DATA_FORMAT_BC3_UNORM_BLOCK:
+ case RD::DATA_FORMAT_BC3_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_DXT5;
+ r_format.rd_format = RD::DATA_FORMAT_BC3_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_BC3_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+ } break; //bc3
+ case RD::DATA_FORMAT_BC4_UNORM_BLOCK: {
+ r_format.image_format = Image::FORMAT_RGTC_R;
+ r_format.rd_format = RD::DATA_FORMAT_BC4_UNORM_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+
+ } break;
+ case RD::DATA_FORMAT_BC5_UNORM_BLOCK: {
+ r_format.image_format = Image::FORMAT_RGTC_RG;
+ r_format.rd_format = RD::DATA_FORMAT_BC5_UNORM_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+
+ } break;
+ case RD::DATA_FORMAT_BC7_UNORM_BLOCK:
+ case RD::DATA_FORMAT_BC7_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_BPTC_RGBA;
+ r_format.rd_format = RD::DATA_FORMAT_BC7_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_BC7_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+
+ } break; //btpc bc7
+ case RD::DATA_FORMAT_BC6H_SFLOAT_BLOCK: {
+ r_format.image_format = Image::FORMAT_BPTC_RGBF;
+ r_format.rd_format = RD::DATA_FORMAT_BC6H_SFLOAT_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break; //float bc6h
+ case RD::DATA_FORMAT_BC6H_UFLOAT_BLOCK: {
+ r_format.image_format = Image::FORMAT_BPTC_RGBFU;
+ r_format.rd_format = RD::DATA_FORMAT_BC6H_UFLOAT_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break; //unsigned float bc6hu
+ case RD::DATA_FORMAT_EAC_R11_UNORM_BLOCK: {
+ r_format.image_format = Image::FORMAT_ETC2_R11;
+ r_format.rd_format = RD::DATA_FORMAT_EAC_R11_UNORM_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+
+ } break; //etc2
+ case RD::DATA_FORMAT_EAC_R11_SNORM_BLOCK: {
+ r_format.image_format = Image::FORMAT_ETC2_R11S;
+ r_format.rd_format = RD::DATA_FORMAT_EAC_R11_SNORM_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break; //signed: {} break; NOT srgb.
+ case RD::DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: {
+ r_format.image_format = Image::FORMAT_ETC2_RG11;
+ r_format.rd_format = RD::DATA_FORMAT_EAC_R11G11_UNORM_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break;
+ case RD::DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: {
+ r_format.image_format = Image::FORMAT_ETC2_RG11S;
+ r_format.rd_format = RD::DATA_FORMAT_EAC_R11G11_SNORM_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break;
+ case RD::DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+ case RD::DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_ETC2_RGB8;
+ r_format.rd_format = RD::DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+
+ } break;
+ /* already maps to FORMAT_ETC2_RGBA8
+ case RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+ case RD::DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_ETC2_RGBA8;
+ r_format.rd_format = RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+ } break;
+ */
+ case RD::DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+ case RD::DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_ETC2_RGB8A1;
+ r_format.rd_format = RD::DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+ } break;
+ case RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+ case RD::DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_ETC2_RA_AS_RG;
+ r_format.rd_format = RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_A;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break;
+ /* already maps to FORMAT_DXT5
+ case RD::DATA_FORMAT_BC3_UNORM_BLOCK:
+ case RD::DATA_FORMAT_BC3_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_DXT5_RA_AS_RG;
+ r_format.rd_format = RD::DATA_FORMAT_BC3_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_BC3_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_A;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
+ } break;
+ */
+ case RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: {
+ // Q: Do we do as we do below, just create the sRGB variant?
+ r_format.image_format = Image::FORMAT_ASTC_4x4;
+ r_format.rd_format = RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+ } break;
+ case RD::DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_ASTC_4x4_HDR;
+ r_format.rd_format = RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_ASTC_4x4_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+
+ } break; // astc 4x4
+ case RD::DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: {
+ // Q: Do we do as we do below, just create the sRGB variant?
+ r_format.image_format = Image::FORMAT_ASTC_8x8;
+ r_format.rd_format = RD::DATA_FORMAT_ASTC_8x8_UNORM_BLOCK;
+ } break;
+ case RD::DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: {
+ r_format.image_format = Image::FORMAT_ASTC_8x8_HDR;
+ r_format.rd_format = RD::DATA_FORMAT_ASTC_8x8_UNORM_BLOCK;
+ r_format.rd_format_srgb = RD::DATA_FORMAT_ASTC_8x8_SRGB_BLOCK;
+ r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+
+ } break; // astc 8x8
+
+ default: {
+ ERR_FAIL_MSG("Unsupported image format");
+ }
+ }
+}
+
/* DECAL API */
RID TextureStorage::decal_atlas_get_texture() const {
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index 8f021f3179..6df6faa40a 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -196,6 +196,27 @@ private:
Ref<Image> _validate_texture_format(const Ref<Image> &p_image, TextureToRDFormat &r_format);
void _texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0, bool p_immediate = false);
+ struct TextureFromRDFormat {
+ Image::Format image_format;
+ RD::DataFormat rd_format;
+ RD::DataFormat rd_format_srgb;
+ RD::TextureSwizzle swizzle_r;
+ RD::TextureSwizzle swizzle_g;
+ RD::TextureSwizzle swizzle_b;
+ RD::TextureSwizzle swizzle_a;
+ TextureFromRDFormat() {
+ image_format = Image::FORMAT_MAX;
+ rd_format = RD::DATA_FORMAT_MAX;
+ rd_format_srgb = RD::DATA_FORMAT_MAX;
+ swizzle_r = RD::TEXTURE_SWIZZLE_R;
+ swizzle_g = RD::TEXTURE_SWIZZLE_G;
+ swizzle_b = RD::TEXTURE_SWIZZLE_B;
+ swizzle_a = RD::TEXTURE_SWIZZLE_A;
+ }
+ };
+
+ void _texture_format_from_rd(RD::DataFormat p_rd_format, TextureFromRDFormat &r_format);
+
/* DECAL API */
struct DecalAtlas {
@@ -488,6 +509,8 @@ public:
virtual void texture_set_path(RID p_texture, const String &p_path) override;
virtual String texture_get_path(RID p_texture) const override;
+ virtual Image::Format texture_get_format(RID p_texture) const override;
+
virtual void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override;
virtual void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override;
virtual void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) override;
@@ -498,6 +521,7 @@ public:
virtual Size2 texture_size_with_proxy(RID p_proxy) override;
+ virtual void texture_rd_initialize(RID p_texture, const RID &p_rd_texture, const RS::TextureLayeredType p_layer_type = RS::TEXTURE_LAYERED_2D_ARRAY) override;
virtual RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) const override;
virtual uint64_t texture_get_native_handle(RID p_texture, bool p_srgb = false) const override;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index c5eabba326..44b15ee91d 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -3994,11 +3994,12 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) {
}
void RendererSceneCull::update_dirty_instances() {
- RSG::utilities->update_dirty_resources();
-
while (_instance_update_list.first()) {
_update_dirty_instance(_instance_update_list.first()->self());
}
+
+ // Update dirty resources after dirty instances as instance updates may affect resources.
+ RSG::utilities->update_dirty_resources();
}
void RendererSceneCull::update() {
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 71f6a28671..7a829826d2 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -180,7 +180,19 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
// to compensate for the loss of sharpness.
const float texture_mipmap_bias = log2f(MIN(scaling_3d_scale, 1.0)) + p_viewport->texture_mipmap_bias;
- p_viewport->render_buffers->configure(p_viewport->render_target, Size2i(render_width, render_height), Size2(width, height), scaling_3d_mode, p_viewport->fsr_sharpness, texture_mipmap_bias, p_viewport->msaa_3d, p_viewport->screen_space_aa, p_viewport->use_taa, p_viewport->use_debanding, p_viewport->view_count);
+ RenderSceneBuffersConfiguration rb_config;
+ rb_config.set_render_target(p_viewport->render_target);
+ rb_config.set_internal_size(Size2i(render_width, render_height));
+ rb_config.set_target_size(Size2(width, height));
+ rb_config.set_view_count(p_viewport->view_count);
+ rb_config.set_scaling_3d_mode(scaling_3d_mode);
+ rb_config.set_msaa_3d(p_viewport->msaa_3d);
+ rb_config.set_screen_space_aa(p_viewport->screen_space_aa);
+ rb_config.set_fsr_sharpness(p_viewport->fsr_sharpness);
+ rb_config.set_texture_mipmap_bias(texture_mipmap_bias);
+ rb_config.set_use_taa(p_viewport->use_taa);
+
+ p_viewport->render_buffers->configure(&rb_config);
}
}
}
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index d87c09a857..1b0a3e9d0f 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -114,6 +114,14 @@ RID RenderingDevice::_texture_create_shared_from_slice(const Ref<RDTextureView>
return texture_create_shared_from_slice(p_view->base, p_with_texture, p_layer, p_mipmap, p_mipmaps, p_slice_type);
}
+Ref<RDTextureFormat> RenderingDevice::_texture_get_format(RID p_rd_texture) {
+ Ref<RDTextureFormat> rtf;
+ rtf.instantiate();
+ rtf->base = texture_get_format(p_rd_texture);
+
+ return rtf;
+}
+
RenderingDevice::FramebufferFormatID RenderingDevice::_framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments, uint32_t p_view_count) {
Vector<AttachmentFormat> attachments;
attachments.resize(p_attachments.size());
@@ -720,6 +728,7 @@ void RenderingDevice::_bind_methods() {
ClassDB::bind_method(D_METHOD("texture_clear", "texture", "color", "base_mipmap", "mipmap_count", "base_layer", "layer_count", "post_barrier"), &RenderingDevice::texture_clear, DEFVAL(BARRIER_MASK_ALL_BARRIERS));
ClassDB::bind_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture", "post_barrier"), &RenderingDevice::texture_resolve_multisample, DEFVAL(BARRIER_MASK_ALL_BARRIERS));
+ ClassDB::bind_method(D_METHOD("texture_get_format", "texture"), &RenderingDevice::_texture_get_format);
ClassDB::bind_method(D_METHOD("texture_get_native_handle", "texture"), &RenderingDevice::texture_get_native_handle);
ClassDB::bind_method(D_METHOD("framebuffer_format_create", "attachments", "view_count"), &RenderingDevice::_framebuffer_format_create, DEFVAL(1));
@@ -745,7 +754,9 @@ void RenderingDevice::_bind_methods() {
ClassDB::bind_method(D_METHOD("shader_compile_spirv_from_source", "shader_source", "allow_cache"), &RenderingDevice::_shader_compile_spirv_from_source, DEFVAL(true));
ClassDB::bind_method(D_METHOD("shader_compile_binary_from_spirv", "spirv_data", "name"), &RenderingDevice::_shader_compile_binary_from_spirv, DEFVAL(""));
ClassDB::bind_method(D_METHOD("shader_create_from_spirv", "spirv_data", "name"), &RenderingDevice::_shader_create_from_spirv, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("shader_create_from_bytecode", "binary_data"), &RenderingDevice::shader_create_from_bytecode);
+ ClassDB::bind_method(D_METHOD("shader_create_from_bytecode", "binary_data", "placeholder_rid"), &RenderingDevice::shader_create_from_bytecode, DEFVAL(RID()));
+ ClassDB::bind_method(D_METHOD("shader_create_placeholder"), &RenderingDevice::shader_create_placeholder);
+
ClassDB::bind_method(D_METHOD("shader_get_vertex_input_attribute_mask", "shader"), &RenderingDevice::shader_get_vertex_input_attribute_mask);
ClassDB::bind_method(D_METHOD("uniform_buffer_create", "size_bytes", "data"), &RenderingDevice::uniform_buffer_create, DEFVAL(Vector<uint8_t>()));
@@ -1073,9 +1084,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..58da3df577 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_VERTEX = 1,
+ BARRIER_MASK_FRAGMENT = 8,
BARRIER_MASK_COMPUTE = 2,
BARRIER_MASK_TRANSFER = 4,
- BARRIER_MASK_ALL_BARRIERS = BARRIER_MASK_RASTER | BARRIER_MASK_COMPUTE | BARRIER_MASK_TRANSFER, // 7
- BARRIER_MASK_NO_BARRIER = 8,
+
+ BARRIER_MASK_RASTER = BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT, // 9,
+ BARRIER_MASK_ALL_BARRIERS = 0x7FFF, // all flags set
+ BARRIER_MASK_NO_BARRIER = 0x8000,
};
/*****************/
@@ -538,6 +541,7 @@ public:
virtual bool texture_is_format_supported_for_usage(DataFormat p_format, BitField<RenderingDevice::TextureUsageBits> p_usage) const = 0;
virtual bool texture_is_shared(RID p_texture) = 0;
virtual bool texture_is_valid(RID p_texture) = 0;
+ virtual TextureFormat texture_get_format(RID p_texture) = 0;
virtual Size2i texture_size(RID p_texture) = 0;
virtual uint64_t texture_get_native_handle(RID p_texture) = 0;
@@ -730,7 +734,8 @@ public:
virtual Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = "") = 0;
virtual RID shader_create_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = "");
- virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary) = 0;
+ virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID()) = 0;
+ virtual RID shader_create_placeholder() = 0;
virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader) = 0;
@@ -1313,6 +1318,7 @@ protected:
RID _texture_create(const Ref<RDTextureFormat> &p_format, const Ref<RDTextureView> &p_view, const TypedArray<PackedByteArray> &p_data = Array());
RID _texture_create_shared(const Ref<RDTextureView> &p_view, RID p_with_texture);
RID _texture_create_shared_from_slice(const Ref<RDTextureView> &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D);
+ Ref<RDTextureFormat> _texture_get_format(RID p_rd_texture);
FramebufferFormatID _framebuffer_format_create(const TypedArray<RDAttachmentFormat> &p_attachments, uint32_t p_view_count);
FramebufferFormatID _framebuffer_format_create_multipass(const TypedArray<RDAttachmentFormat> &p_attachments, const TypedArray<RDFramebufferPass> &p_passes, uint32_t p_view_count);
diff --git a/servers/rendering/rendering_device_binds.h b/servers/rendering/rendering_device_binds.h
index e173400299..bf30efbcf6 100644
--- a/servers/rendering/rendering_device_binds.h
+++ b/servers/rendering/rendering_device_binds.h
@@ -53,7 +53,9 @@
class RDTextureFormat : public RefCounted {
GDCLASS(RDTextureFormat, RefCounted)
+
friend class RenderingDevice;
+ friend class RenderSceneBuffersRD;
RD::TextureFormat base;
@@ -91,6 +93,7 @@ class RDTextureView : public RefCounted {
GDCLASS(RDTextureView, RefCounted)
friend class RenderingDevice;
+ friend class RenderSceneBuffersRD;
RD::TextureView base;
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 65bdb94c9f..c4464cb180 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;
}
@@ -398,6 +386,12 @@ void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) {
}
}
+void RenderingServerDefault::_call_on_render_thread(const Callable &p_callable) {
+ Variant ret;
+ Callable::CallError ce;
+ p_callable.callp(nullptr, 0, ret, ce);
+}
+
RenderingServerDefault::RenderingServerDefault(bool p_create_thread) :
command_queue(p_create_thread) {
RenderingServer::init();
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 797fe73ba0..e9b40eb082 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -97,6 +97,8 @@ class RenderingServerDefault : public RenderingServer {
void _free(RID p_rid);
+ void _call_on_render_thread(const Callable &p_callable);
+
public:
//if editor is redrawing when it shouldn't, enable this and put a breakpoint in _changes_changed()
//#define DEBUG_CHANGES
@@ -210,9 +212,13 @@ public:
FUNC2(texture_set_path, RID, const String &)
FUNC1RC(String, texture_get_path, RID)
+
+ FUNC1RC(Image::Format, texture_get_format, RID)
+
FUNC1(texture_debug_usage, List<TextureInfo> *)
FUNC2(texture_set_force_redraw_if_visible, RID, bool)
+ FUNCRIDTEX2(texture_rd, const RID &, const RS::TextureLayeredType)
FUNC2RC(RID, texture_get_rd_texture, RID, bool)
FUNC2RC(uint64_t, texture_get_native_handle, RID, bool)
@@ -946,9 +952,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,19 +993,14 @@ 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;
+ virtual void call_on_render_thread(const Callable &p_callable) override {
+ if (Thread::get_caller_id() == server_thread) {
+ command_queue.flush_if_pending();
+ _call_on_render_thread(p_callable);
+ } else {
+ command_queue.push(this, &RenderingServerDefault::_call_on_render_thread, p_callable);
+ }
+ }
/* TESTING */
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 1515ed32f6..0e6c0e4bf6 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -283,7 +283,21 @@ String ShaderCompiler::_get_sampler_name(ShaderLanguage::TextureFilter p_filter,
ERR_FAIL_COND_V(actions.default_repeat == ShaderLanguage::REPEAT_DEFAULT, String());
p_repeat = actions.default_repeat;
}
- return actions.sampler_array_name + "[" + itos(p_filter + (p_repeat == ShaderLanguage::REPEAT_ENABLE ? ShaderLanguage::FILTER_DEFAULT : 0)) + "]";
+ constexpr const char *name_mapping[] = {
+ "SAMPLER_NEAREST_CLAMP",
+ "SAMPLER_LINEAR_CLAMP",
+ "SAMPLER_NEAREST_WITH_MIPMAPS_CLAMP",
+ "SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP",
+ "SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_CLAMP",
+ "SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_CLAMP",
+ "SAMPLER_NEAREST_REPEAT",
+ "SAMPLER_LINEAR_REPEAT",
+ "SAMPLER_NEAREST_WITH_MIPMAPS_REPEAT",
+ "SAMPLER_LINEAR_WITH_MIPMAPS_REPEAT",
+ "SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT",
+ "SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT"
+ };
+ return String(name_mapping[p_filter + (p_repeat == ShaderLanguage::REPEAT_ENABLE ? ShaderLanguage::FILTER_DEFAULT : 0)]);
}
void ShaderCompiler::_dump_function_deps(const SL::ShaderNode *p_node, const StringName &p_for_func, const HashMap<StringName, String> &p_func_code, String &r_to_add, HashSet<StringName> &added) {
@@ -909,7 +923,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
//its a uniform!
const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[vnode->name];
if (u.texture_order >= 0) {
- StringName name = vnode->name;
+ StringName name;
if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) {
name = "color_buffer";
if (u.filter >= ShaderLanguage::FILTER_NEAREST_MIPMAP) {
diff --git a/servers/rendering/shader_compiler.h b/servers/rendering/shader_compiler.h
index 3bb29a545b..2d58be7261 100644
--- a/servers/rendering/shader_compiler.h
+++ b/servers/rendering/shader_compiler.h
@@ -93,7 +93,6 @@ public:
HashMap<StringName, String> custom_samplers;
ShaderLanguage::TextureFilter default_filter;
ShaderLanguage::TextureRepeat default_repeat;
- String sampler_array_name;
int base_texture_binding_index = 0;
int texture_layout_set = 0;
String base_uniform_string;
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 22e6f7d67d..e9421a435e 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -3388,12 +3388,14 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
int last_arg_count = 0;
+ bool exists = false;
String arg_list = "";
for (int i = 0; i < shader->functions.size(); i++) {
if (name != shader->functions[i].name) {
continue;
}
+ exists = true;
if (!shader->functions[i].callable) {
_set_error(vformat(RTR("Function '%s' can't be called from source code."), String(name)));
@@ -3494,10 +3496,12 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
}
}
- if (last_arg_count > args.size()) {
- _set_error(vformat(RTR("Too few arguments for \"%s(%s)\" call. Expected at least %d but received %d."), String(name), arg_list, last_arg_count, args.size()));
- } else if (last_arg_count < args.size()) {
- _set_error(vformat(RTR("Too many arguments for \"%s(%s)\" call. Expected at most %d but received %d."), String(name), arg_list, last_arg_count, args.size()));
+ if (exists) {
+ if (last_arg_count > args.size()) {
+ _set_error(vformat(RTR("Too few arguments for \"%s(%s)\" call. Expected at least %d but received %d."), String(name), arg_list, last_arg_count, args.size()));
+ } else if (last_arg_count < args.size()) {
+ _set_error(vformat(RTR("Too many arguments for \"%s(%s)\" call. Expected at most %d but received %d."), String(name), arg_list, last_arg_count, args.size()));
+ }
}
return false;
@@ -8619,7 +8623,12 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
}
if (uniform.array_size > 0) {
- if (tk.type != TK_HINT_SOURCE_COLOR) {
+ static Vector<int> supported_hints = {
+ TK_HINT_SOURCE_COLOR, TK_REPEAT_DISABLE, TK_REPEAT_ENABLE,
+ TK_FILTER_LINEAR, TK_FILTER_LINEAR_MIPMAP, TK_FILTER_LINEAR_MIPMAP_ANISOTROPIC,
+ TK_FILTER_NEAREST, TK_FILTER_NEAREST_MIPMAP, TK_FILTER_NEAREST_MIPMAP_ANISOTROPIC
+ };
+ if (!supported_hints.has(tk.type)) {
_set_error(RTR("This hint is not supported for uniform arrays."));
return ERR_PARSE_ERROR;
}
@@ -10544,19 +10553,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");
@@ -10574,10 +10587,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/servers/rendering/storage/render_scene_buffers.cpp b/servers/rendering/storage/render_scene_buffers.cpp
index 6369139aa6..19496c0116 100644
--- a/servers/rendering/storage/render_scene_buffers.cpp
+++ b/servers/rendering/storage/render_scene_buffers.cpp
@@ -30,22 +30,67 @@
#include "render_scene_buffers.h"
+void RenderSceneBuffersConfiguration::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_render_target"), &RenderSceneBuffersConfiguration::get_render_target);
+ ClassDB::bind_method(D_METHOD("set_render_target", "render_target"), &RenderSceneBuffersConfiguration::set_render_target);
+ ADD_PROPERTY(PropertyInfo(Variant::RID, "render_target"), "set_render_target", "get_render_target");
+
+ ClassDB::bind_method(D_METHOD("get_internal_size"), &RenderSceneBuffersConfiguration::get_internal_size);
+ ClassDB::bind_method(D_METHOD("set_internal_size", "internal_size"), &RenderSceneBuffersConfiguration::set_internal_size);
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "internal_size"), "set_internal_size", "get_internal_size");
+
+ ClassDB::bind_method(D_METHOD("get_target_size"), &RenderSceneBuffersConfiguration::get_target_size);
+ ClassDB::bind_method(D_METHOD("set_target_size", "target_size"), &RenderSceneBuffersConfiguration::set_target_size);
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "target_size"), "set_target_size", "get_target_size");
+
+ ClassDB::bind_method(D_METHOD("get_view_count"), &RenderSceneBuffersConfiguration::get_view_count);
+ ClassDB::bind_method(D_METHOD("set_view_count", "view_count"), &RenderSceneBuffersConfiguration::set_view_count);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "view_count"), "set_view_count", "get_view_count");
+
+ ClassDB::bind_method(D_METHOD("get_scaling_3d_mode"), &RenderSceneBuffersConfiguration::get_scaling_3d_mode);
+ ClassDB::bind_method(D_METHOD("set_scaling_3d_mode", "scaling_3d_mode"), &RenderSceneBuffersConfiguration::set_scaling_3d_mode);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode"); // TODO VIEWPORT_SCALING_3D_MODE_OFF is possible here too, but we can't specify an enum string for it.
+
+ ClassDB::bind_method(D_METHOD("get_msaa_3d"), &RenderSceneBuffersConfiguration::get_msaa_3d);
+ ClassDB::bind_method(D_METHOD("set_msaa_3d", "msaa_3d"), &RenderSceneBuffersConfiguration::set_msaa_3d);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_3d", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x"), "set_msaa_3d", "get_msaa_3d");
+
+ ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &RenderSceneBuffersConfiguration::get_screen_space_aa);
+ ClassDB::bind_method(D_METHOD("set_screen_space_aa", "screen_space_aa"), &RenderSceneBuffersConfiguration::set_screen_space_aa);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled,FXAA"), "set_screen_space_aa", "get_screen_space_aa");
+
+ ClassDB::bind_method(D_METHOD("get_fsr_sharpness"), &RenderSceneBuffersConfiguration::get_fsr_sharpness);
+ ClassDB::bind_method(D_METHOD("set_fsr_sharpness", "fsr_sharpness"), &RenderSceneBuffersConfiguration::set_fsr_sharpness);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fsr_sharpness"), "set_fsr_sharpness", "get_fsr_sharpness");
+
+ ClassDB::bind_method(D_METHOD("get_texture_mipmap_bias"), &RenderSceneBuffersConfiguration::get_texture_mipmap_bias);
+ ClassDB::bind_method(D_METHOD("set_texture_mipmap_bias", "texture_mipmap_bias"), &RenderSceneBuffersConfiguration::set_texture_mipmap_bias);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "texture_mipmap_bias"), "set_texture_mipmap_bias", "get_texture_mipmap_bias");
+}
+
void RenderSceneBuffers::_bind_methods() {
- ClassDB::bind_method(D_METHOD("configure", "render_target", "internal_size", "target_size", "fsr_sharpness", "texture_mipmap_bias", "msaa", "screen_space_aa", "use_taa", "use_debanding", "view_count"), &RenderSceneBuffers::configure);
+ ClassDB::bind_method(D_METHOD("configure", "config"), &RenderSceneBuffers::configure);
+}
+
+void RenderSceneBuffersExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_configure, "config");
+ GDVIRTUAL_BIND(_set_fsr_sharpness, "fsr_sharpness");
+ GDVIRTUAL_BIND(_set_texture_mipmap_bias, "texture_mipmap_bias");
+ GDVIRTUAL_BIND(_set_use_debanding, "use_debanding");
}
-void RenderSceneBuffers::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) {
- GDVIRTUAL_CALL(_configure, p_render_target, p_internal_size, p_target_size, p_scaling_3d_mode, p_fsr_sharpness, p_texture_mipmap_bias, p_msaa, p_screen_space_aa, p_use_taa, p_use_debanding, p_view_count);
+void RenderSceneBuffersExtension::configure(const RenderSceneBuffersConfiguration *p_config) {
+ GDVIRTUAL_CALL(_configure, p_config);
};
-void RenderSceneBuffers::set_fsr_sharpness(float p_fsr_sharpness) {
+void RenderSceneBuffersExtension::set_fsr_sharpness(float p_fsr_sharpness) {
GDVIRTUAL_CALL(_set_fsr_sharpness, p_fsr_sharpness);
}
-void RenderSceneBuffers::set_texture_mipmap_bias(float p_texture_mipmap_bias) {
+void RenderSceneBuffersExtension::set_texture_mipmap_bias(float p_texture_mipmap_bias) {
GDVIRTUAL_CALL(_set_texture_mipmap_bias, p_texture_mipmap_bias);
}
-void RenderSceneBuffers::set_use_debanding(bool p_use_debanding) {
+void RenderSceneBuffersExtension::set_use_debanding(bool p_use_debanding) {
GDVIRTUAL_CALL(_set_use_debanding, p_use_debanding);
}
diff --git a/servers/rendering/storage/render_scene_buffers.h b/servers/rendering/storage/render_scene_buffers.h
index cf96a9f372..6c82d47799 100644
--- a/servers/rendering/storage/render_scene_buffers.h
+++ b/servers/rendering/storage/render_scene_buffers.h
@@ -34,27 +34,103 @@
#include "core/object/ref_counted.h"
#include "servers/rendering_server.h"
+class RenderSceneBuffersConfiguration : public RefCounted {
+ GDCLASS(RenderSceneBuffersConfiguration, RefCounted);
+
+private:
+ RID render_target;
+
+ Size2i internal_size;
+ Size2i target_size;
+ uint32_t view_count = 1;
+
+ RS::ViewportScaling3DMode scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
+ RS::ViewportMSAA msaa_3d = RS::VIEWPORT_MSAA_DISABLED;
+ RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED;
+
+ float fsr_sharpness = 0.0;
+ float texture_mipmap_bias = 0.0;
+ bool use_taa = false;
+ bool use_debanding = false;
+
+protected:
+ static void _bind_methods();
+
+public:
+ RID get_render_target() const { return render_target; }
+ void set_render_target(RID p_render_target) { render_target = p_render_target; }
+
+ Size2i get_internal_size() const { return internal_size; }
+ void set_internal_size(Size2i p_internal_size) { internal_size = p_internal_size; }
+
+ Size2i get_target_size() const { return target_size; }
+ void set_target_size(Size2i p_target_size) { target_size = p_target_size; }
+
+ uint32_t get_view_count() const { return view_count; }
+ void set_view_count(uint32_t p_view_count) { view_count = p_view_count; }
+
+ RS::ViewportScaling3DMode get_scaling_3d_mode() const { return scaling_3d_mode; }
+ void set_scaling_3d_mode(RS::ViewportScaling3DMode p_scaling_3d_mode) { scaling_3d_mode = p_scaling_3d_mode; }
+
+ RS::ViewportMSAA get_msaa_3d() const { return msaa_3d; }
+ void set_msaa_3d(RS::ViewportMSAA p_msaa_3d) { msaa_3d = p_msaa_3d; }
+
+ RS::ViewportScreenSpaceAA get_screen_space_aa() const { return screen_space_aa; }
+ void set_screen_space_aa(RS::ViewportScreenSpaceAA p_screen_space_aa) { screen_space_aa = p_screen_space_aa; }
+
+ float get_fsr_sharpness() const { return fsr_sharpness; }
+ void set_fsr_sharpness(float p_fsr_sharpness) { fsr_sharpness = p_fsr_sharpness; }
+
+ float get_texture_mipmap_bias() const { return texture_mipmap_bias; }
+ void set_texture_mipmap_bias(float p_texture_mipmap_bias) { texture_mipmap_bias = p_texture_mipmap_bias; }
+
+ bool get_use_taa() const { return use_taa; }
+ void set_use_taa(bool p_use_taa) { use_taa = p_use_taa; }
+
+ bool get_use_debanding() const { return use_debanding; }
+ void set_use_debanding(bool p_use_debanding) { use_debanding = p_use_debanding; }
+
+ RenderSceneBuffersConfiguration() {}
+ virtual ~RenderSceneBuffersConfiguration(){};
+};
+
class RenderSceneBuffers : public RefCounted {
GDCLASS(RenderSceneBuffers, RefCounted);
protected:
static void _bind_methods();
- GDVIRTUAL11(_configure, RID, Size2i, Size2i, RS::ViewportScaling3DMode, float, float, RS::ViewportMSAA, RenderingServer::ViewportScreenSpaceAA, bool, bool, uint32_t)
+public:
+ RenderSceneBuffers(){};
+ virtual ~RenderSceneBuffers(){};
+
+ virtual void configure(const RenderSceneBuffersConfiguration *p_config) = 0;
+
+ // for those settings that are unlikely to require buffers to be recreated, we'll add setters
+ virtual void set_fsr_sharpness(float p_fsr_sharpness) = 0;
+ virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) = 0;
+ virtual void set_use_debanding(bool p_use_debanding) = 0;
+};
+
+class RenderSceneBuffersExtension : public RenderSceneBuffers {
+ GDCLASS(RenderSceneBuffersExtension, RenderSceneBuffers);
+
+protected:
+ static void _bind_methods();
+
+ GDVIRTUAL1(_configure, const RenderSceneBuffersConfiguration *)
GDVIRTUAL1(_set_fsr_sharpness, float)
GDVIRTUAL1(_set_texture_mipmap_bias, float)
GDVIRTUAL1(_set_use_debanding, bool)
public:
- RenderSceneBuffers(){};
- virtual ~RenderSceneBuffers(){};
+ virtual ~RenderSceneBuffersExtension(){};
- virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, RS::ViewportScaling3DMode p_scaling_3d_mode, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa_3d, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count);
+ virtual void configure(const RenderSceneBuffersConfiguration *p_config) override;
- // for those settings that are unlikely to require buffers to be recreated, we'll add setters
- virtual void set_fsr_sharpness(float p_fsr_sharpness);
- virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias);
- virtual void set_use_debanding(bool p_use_debanding);
+ virtual void set_fsr_sharpness(float p_fsr_sharpness) override;
+ virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override;
+ virtual void set_use_debanding(bool p_use_debanding) override;
};
#endif // RENDER_SCENE_BUFFERS_H
diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h
index 93b32bd372..c3a257595c 100644
--- a/servers/rendering/storage/texture_storage.h
+++ b/servers/rendering/storage/texture_storage.h
@@ -90,6 +90,8 @@ public:
virtual void texture_set_path(RID p_texture, const String &p_path) = 0;
virtual String texture_get_path(RID p_texture) const = 0;
+ virtual Image::Format texture_get_format(RID p_texture) const = 0;
+
virtual void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) = 0;
virtual void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) = 0;
virtual void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) = 0;
@@ -100,6 +102,7 @@ public:
virtual Size2 texture_size_with_proxy(RID p_proxy) = 0;
+ virtual void texture_rd_initialize(RID p_texture, const RID &p_rd_texture, const RS::TextureLayeredType p_layer_type = RS::TEXTURE_LAYERED_2D_ARRAY) = 0;
virtual RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) const = 0;
virtual uint64_t texture_get_native_handle(RID p_texture, bool p_srgb = false) const = 0;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 4c6b765157..48b38cf2b7 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -1697,7 +1697,10 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("texture_set_path", "texture", "path"), &RenderingServer::texture_set_path);
ClassDB::bind_method(D_METHOD("texture_get_path", "texture"), &RenderingServer::texture_get_path);
+ ClassDB::bind_method(D_METHOD("texture_get_format", "texture"), &RenderingServer::texture_get_format);
+
ClassDB::bind_method(D_METHOD("texture_set_force_redraw_if_visible", "texture", "enable"), &RenderingServer::texture_set_force_redraw_if_visible);
+ ClassDB::bind_method(D_METHOD("texture_rd_create", "rd_texture", "layer_type"), &RenderingServer::texture_rd_create, DEFVAL(RenderingServer::TEXTURE_LAYERED_2D_ARRAY));
ClassDB::bind_method(D_METHOD("texture_get_rd_texture", "texture", "srgb"), &RenderingServer::texture_get_rd_texture, DEFVAL(false));
ClassDB::bind_method(D_METHOD("texture_get_native_handle", "texture", "srgb"), &RenderingServer::texture_get_native_handle, DEFVAL(false));
@@ -2808,6 +2811,8 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("force_draw", "swap_buffers", "frame_step"), &RenderingServer::draw, DEFVAL(true), DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("get_rendering_device"), &RenderingServer::get_rendering_device);
ClassDB::bind_method(D_METHOD("create_local_rendering_device"), &RenderingServer::create_local_rendering_device);
+
+ ClassDB::bind_method(D_METHOD("call_on_render_thread", "callable"), &RenderingServer::call_on_render_thread);
}
void RenderingServer::mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry3D::MeshData &p_mesh_data) {
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index deac2a59f9..618ceb3091 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -127,6 +127,8 @@ public:
virtual void texture_set_path(RID p_texture, const String &p_path) = 0;
virtual String texture_get_path(RID p_texture) const = 0;
+ virtual Image::Format texture_get_format(RID p_texture) const = 0;
+
typedef void (*TextureDetectCallback)(void *);
virtual void texture_set_detect_3d_callback(RID p_texture, TextureDetectCallback p_callback, void *p_userdata) = 0;
@@ -158,6 +160,7 @@ public:
virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) = 0;
+ virtual RID texture_rd_create(const RID &p_rd_texture, const RenderingServer::TextureLayeredType p_layer_type = RenderingServer::TEXTURE_LAYERED_2D_ARRAY) = 0;
virtual RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) const = 0;
virtual uint64_t texture_get_native_handle(RID p_texture, bool p_srgb = false) const = 0;
@@ -1599,6 +1602,8 @@ public:
bool is_render_loop_enabled() const;
void set_render_loop_enabled(bool p_enabled);
+ virtual void call_on_render_thread(const Callable &p_callable) = 0;
+
RenderingServer();
virtual ~RenderingServer();
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index 329344d4c4..07ad14f120 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -1539,7 +1539,7 @@ void TextServer::shaped_text_draw_outline(const RID &p_shaped, const RID &p_canv
if (rtl && ellipsis_pos >= 0) {
for (int i = ellipsis_gl_size - 1; i >= 0; i--) {
for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) {
- font_draw_glyph(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color);
+ font_draw_glyph_outline(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, p_outline_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color);
if (orientation == ORIENTATION_HORIZONTAL) {
ofs.x += ellipsis_glyphs[i].advance;
} else {
@@ -1602,7 +1602,7 @@ void TextServer::shaped_text_draw_outline(const RID &p_shaped, const RID &p_canv
if (!rtl && ellipsis_pos >= 0) {
for (int i = 0; i < ellipsis_gl_size; i++) {
for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) {
- font_draw_glyph(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color);
+ font_draw_glyph_outline(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, p_outline_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color);
if (orientation == ORIENTATION_HORIZONTAL) {
ofs.x += ellipsis_glyphs[i].advance;
} else {
diff --git a/tests/core/input/test_input_event.h b/tests/core/input/test_input_event.h
new file mode 100644
index 0000000000..6b4b80486c
--- /dev/null
+++ b/tests/core/input/test_input_event.h
@@ -0,0 +1,115 @@
+/**************************************************************************/
+/* test_input_event.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 TEST_INPUT_EVENT_H
+#define TEST_INPUT_EVENT_H
+
+#include "core/input/input_event.h"
+#include "core/math/rect2.h"
+#include "core/os/memory.h"
+#include "core/variant/array.h"
+
+#include "tests/test_macros.h"
+
+namespace TestInputEvent {
+TEST_CASE("[InputEvent] Signal is emitted when device is changed") {
+ Ref<InputEventKey> input_event;
+ input_event.instantiate();
+
+ SIGNAL_WATCH(*input_event, SNAME("changed"));
+ Array args1;
+ Array empty_args;
+ empty_args.push_back(args1);
+
+ input_event->set_device(1);
+
+ SIGNAL_CHECK("changed", empty_args);
+ CHECK(input_event->get_device() == 1);
+
+ SIGNAL_UNWATCH(*input_event, SNAME("changed"));
+}
+
+TEST_CASE("[InputEvent] Test accumulate") {
+ Ref<InputEventMouseMotion> iemm1, iemm2;
+ Ref<InputEventKey> iek;
+
+ iemm1.instantiate(), iemm2.instantiate();
+ iek.instantiate();
+
+ iemm1->set_button_mask(MouseButtonMask::LEFT);
+
+ CHECK_FALSE(iemm1->accumulate(iemm2));
+
+ iemm2->set_button_mask(MouseButtonMask::LEFT);
+
+ CHECK(iemm1->accumulate(iemm2));
+
+ CHECK_FALSE(iemm1->accumulate(iek));
+ CHECK_FALSE(iemm2->accumulate(iek));
+}
+
+TEST_CASE("[InputEvent][SceneTree] Test methods that interact with the InputMap") {
+ const String mock_action = "mock_action";
+ Ref<InputEventJoypadMotion> iejm;
+ iejm.instantiate();
+
+ InputMap::get_singleton()->add_action(mock_action, 0.5);
+ InputMap::get_singleton()->action_add_event(mock_action, iejm);
+
+ CHECK(iejm->is_action_type());
+ CHECK(iejm->is_action(mock_action));
+
+ CHECK(iejm->is_action_released(mock_action));
+ CHECK(Math::is_equal_approx(iejm->get_action_strength(mock_action), 0.0f));
+
+ iejm->set_axis_value(0.8f);
+ // Since deadzone is 0.5, action_strength grows linearly from 0.5 to 1.0.
+ CHECK(Math::is_equal_approx(iejm->get_action_strength(mock_action), 0.6f));
+ CHECK(Math::is_equal_approx(iejm->get_action_raw_strength(mock_action), 0.8f));
+ CHECK(iejm->is_action_pressed(mock_action));
+
+ InputMap::get_singleton()->erase_action(mock_action);
+}
+
+TEST_CASE("[InputEvent] Test xformed_by") {
+ Ref<InputEventMouseMotion> iemm1;
+ iemm1.instantiate();
+
+ iemm1->set_position(Vector2(0.0f, 0.0f));
+ Transform2D transform;
+ transform = transform.translated(Vector2(2.0f, 3.0f));
+
+ Ref<InputEventMouseMotion> iemm2 = iemm1->xformed_by(transform);
+
+ CHECK(iemm2->get_position().is_equal_approx(Vector2(2.0f, 3.0f)));
+}
+} // namespace TestInputEvent
+
+#endif // TEST_INPUT_EVENT_H
diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h
index d6eaa8bd77..92ab166ae8 100644
--- a/tests/core/io/test_image.h
+++ b/tests/core/io/test_image.h
@@ -115,6 +115,16 @@ TEST_CASE("[Image] Saving and loading") {
image_bmp->load_bmp_from_buffer(data_bmp) == OK,
"The BMP image should load successfully.");
+ // Load DDS
+ Ref<Image> image_dds = memnew(Image());
+ Ref<FileAccess> f_dds = FileAccess::open(TestUtils::get_data_path("images/icon.dds"), FileAccess::READ, &err);
+ PackedByteArray data_dds;
+ data_dds.resize(f_dds->get_length() + 1);
+ f_dds->get_buffer(data_dds.ptrw(), f_dds->get_length());
+ CHECK_MESSAGE(
+ image_dds->load_dds_from_buffer(data_dds) == OK,
+ "The DDS image should load successfully.");
+
// Load JPG
Ref<Image> image_jpg = memnew(Image());
Ref<FileAccess> f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err);
@@ -325,6 +335,86 @@ TEST_CASE("[Image] Modifying pixels of an image") {
CHECK_MESSAGE(gray_image->get_pixel(2, 2).is_equal_approx(Color(0.266666681, 0.266666681, 0.266666681, 1)), "convert() RGBA to L8 should be around 0.266666681 (68).");
}
}
+
+TEST_CASE("[Image] Custom mipmaps") {
+ Ref<Image> image = memnew(Image(100, 100, false, Image::FORMAT_RGBA8));
+
+ REQUIRE(!image->has_mipmaps());
+ image->generate_mipmaps();
+ REQUIRE(image->has_mipmaps());
+
+ const int mipmaps = image->get_mipmap_count() + 1;
+ REQUIRE(mipmaps == 7);
+
+ // Initialize reference mipmap data.
+ // Each byte is given value "mipmap_index * 5".
+
+ {
+ PackedByteArray data = image->get_data();
+ uint8_t *data_ptr = data.ptrw();
+
+ for (int mip = 0; mip < mipmaps; mip++) {
+ int mip_offset = 0;
+ int mip_size = 0;
+ image->get_mipmap_offset_and_size(mip, mip_offset, mip_size);
+
+ for (int i = 0; i < mip_size; i++) {
+ data_ptr[mip_offset + i] = mip * 5;
+ }
+ }
+ image->set_data(image->get_width(), image->get_height(), image->has_mipmaps(), image->get_format(), data);
+ }
+
+ // Byte format conversion.
+
+ for (int format = Image::FORMAT_L8; format <= Image::FORMAT_RGBA8; format++) {
+ Ref<Image> image_bytes = memnew(Image());
+ image_bytes->copy_internals_from(image);
+ image_bytes->convert((Image::Format)format);
+ REQUIRE(image_bytes->has_mipmaps());
+
+ PackedByteArray data = image_bytes->get_data();
+ const uint8_t *data_ptr = data.ptr();
+
+ for (int mip = 0; mip < mipmaps; mip++) {
+ int mip_offset = 0;
+ int mip_size = 0;
+ image_bytes->get_mipmap_offset_and_size(mip, mip_offset, mip_size);
+
+ for (int i = 0; i < mip_size; i++) {
+ if (data_ptr[mip_offset + i] != mip * 5) {
+ REQUIRE_MESSAGE(false, "Byte format conversion error.");
+ }
+ }
+ }
+ }
+
+ // Floating point format conversion.
+
+ for (int format = Image::FORMAT_RF; format <= Image::FORMAT_RGBAF; format++) {
+ Ref<Image> image_rgbaf = memnew(Image());
+ image_rgbaf->copy_internals_from(image);
+ image_rgbaf->convert((Image::Format)format);
+ REQUIRE(image_rgbaf->has_mipmaps());
+
+ PackedByteArray data = image_rgbaf->get_data();
+ const uint8_t *data_ptr = data.ptr();
+
+ for (int mip = 0; mip < mipmaps; mip++) {
+ int mip_offset = 0;
+ int mip_size = 0;
+ image_rgbaf->get_mipmap_offset_and_size(mip, mip_offset, mip_size);
+
+ for (int i = 0; i < mip_size; i += 4) {
+ float value = *(float *)(data_ptr + mip_offset + i);
+ if (!Math::is_equal_approx(value * 255.0f, mip * 5)) {
+ REQUIRE_MESSAGE(false, "Floating point conversion error.");
+ }
+ }
+ }
+ }
+}
+
} // namespace TestImage
#endif // TEST_IMAGE_H
diff --git a/tests/core/io/test_json.h b/tests/core/io/test_json.h
index 34a66ab6b5..73c7ac7a2e 100644
--- a/tests/core/io/test_json.h
+++ b/tests/core/io/test_json.h
@@ -146,6 +146,90 @@ TEST_CASE("[JSON] Parsing objects (dictionaries)") {
dictionary["empty_object"].hash() == Dictionary().hash(),
"The parsed JSON should contain the expected values.");
}
+
+TEST_CASE("[JSON] Parsing escape sequences") {
+ // Only certain escape sequences are valid according to the JSON specification.
+ // Others must result in a parsing error instead.
+
+ JSON json;
+
+ TypedArray<String> valid_escapes;
+ valid_escapes.push_back("\";\"");
+ valid_escapes.push_back("\\;\\");
+ valid_escapes.push_back("/;/");
+ valid_escapes.push_back("b;\b");
+ valid_escapes.push_back("f;\f");
+ valid_escapes.push_back("n;\n");
+ valid_escapes.push_back("r;\r");
+ valid_escapes.push_back("t;\t");
+
+ SUBCASE("Basic valid escape sequences") {
+ for (int i = 0; i < valid_escapes.size(); i++) {
+ String valid_escape = valid_escapes[i];
+ String valid_escape_string = valid_escape.get_slice(";", 0);
+ String valid_escape_value = valid_escape.get_slice(";", 1);
+
+ String json_string = "\"\\";
+ json_string += valid_escape_string;
+ json_string += "\"";
+ json.parse(json_string);
+
+ CHECK_MESSAGE(
+ json.get_error_line() == 0,
+ vformat("Parsing valid escape sequence `%s` as JSON should parse successfully.", valid_escape_string));
+
+ String json_value = json.get_data();
+ CHECK_MESSAGE(
+ json_value == valid_escape_value,
+ vformat("Parsing valid escape sequence `%s` as JSON should return the expected value.", valid_escape_string));
+ }
+ }
+
+ SUBCASE("Valid unicode escape sequences") {
+ String json_string = "\"\\u0000\"";
+ json.parse(json_string);
+
+ CHECK_MESSAGE(
+ json.get_error_line() == 0,
+ vformat("Parsing valid unicode escape sequence with value `0000` as JSON should parse successfully."));
+
+ String json_value = json.get_data();
+ CHECK_MESSAGE(
+ json_value == "\0",
+ vformat("Parsing valid unicode escape sequence with value `0000` as JSON should return the expected value."));
+ }
+
+ SUBCASE("Invalid escape sequences") {
+ for (char32_t i = 0; i < 128; i++) {
+ bool skip = false;
+ for (int j = 0; j < valid_escapes.size(); j++) {
+ String valid_escape = valid_escapes[j];
+ String valid_escape_string = valid_escape.get_slice(";", 0);
+ if (valid_escape_string[0] == i) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (skip) {
+ continue;
+ }
+
+ String json_string = "\"\\";
+ json_string += i;
+ json_string += "\"";
+ Error err = json.parse(json_string);
+
+ // TODO: Line number is currently kept on 0, despite an error occurring. This should be fixed in the JSON parser.
+ // CHECK_MESSAGE(
+ // json.get_error_line() != 0,
+ // vformat("Parsing invalid escape sequence with ASCII value `%d` as JSON should fail to parse.", i));
+ CHECK_MESSAGE(
+ err == ERR_PARSE_ERROR,
+ vformat("Parsing invalid escape sequence with ASCII value `%d` as JSON should fail to parse with ERR_PARSE_ERROR.", i));
+ }
+ }
+}
} // namespace TestJSON
#endif // TEST_JSON_H
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/object/test_object.h b/tests/core/object/test_object.h
index 98f9b3da65..8ab6221a1c 100644
--- a/tests/core/object/test_object.h
+++ b/tests/core/object/test_object.h
@@ -399,6 +399,29 @@ TEST_CASE("[Object] Signals") {
SIGNAL_CHECK("my_custom_signal", empty_signal_args);
SIGNAL_UNWATCH(&object, "my_custom_signal");
}
+
+ SUBCASE("Connecting and then disconnecting many signals should not leave anything behind") {
+ List<Object::Connection> signal_connections;
+ Object targets[100];
+
+ for (int i = 0; i < 10; i++) {
+ ERR_PRINT_OFF;
+ for (Object &target : targets) {
+ object.connect("my_custom_signal", callable_mp(&target, &Object::notify_property_list_changed));
+ }
+ ERR_PRINT_ON;
+ signal_connections.clear();
+ object.get_all_signal_connections(&signal_connections);
+ CHECK(signal_connections.size() == 100);
+ }
+
+ for (Object &target : targets) {
+ object.disconnect("my_custom_signal", callable_mp(&target, &Object::notify_property_list_changed));
+ }
+ signal_connections.clear();
+ object.get_all_signal_connections(&signal_connections);
+ CHECK(signal_connections.size() == 0);
+ }
}
} // namespace TestObject
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/data/images/icon.dds b/tests/data/images/icon.dds
new file mode 100644
index 0000000000..8a9de402cb
--- /dev/null
+++ b/tests/data/images/icon.dds
Binary files differ
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/scene/test_viewport.h b/tests/scene/test_viewport.h
index d76fc40125..a6b3b2c40c 100644
--- a/tests/scene/test_viewport.h
+++ b/tests/scene/test_viewport.h
@@ -33,6 +33,7 @@
#include "scene/2d/node_2d.h"
#include "scene/gui/control.h"
+#include "scene/gui/subviewport_container.h"
#include "scene/main/window.h"
#include "tests/test_macros.h"
@@ -715,6 +716,46 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
memdelete(node_a);
}
+TEST_CASE("[SceneTree][Viewport] Control mouse cursor shape") {
+ SUBCASE("[Viewport][CursorShape] Mouse cursor is not overridden by SubViewportContainer") {
+ SubViewportContainer *node_a = memnew(SubViewportContainer);
+ SubViewport *node_b = memnew(SubViewport);
+ Control *node_c = memnew(Control);
+
+ node_a->set_name("SubViewportContainer");
+ node_b->set_name("SubViewport");
+ node_c->set_name("Control");
+ node_a->set_position(Point2i(0, 0));
+ node_c->set_position(Point2i(0, 0));
+ node_a->set_size(Point2i(100, 100));
+ node_b->set_size(Point2i(100, 100));
+ node_c->set_size(Point2i(100, 100));
+ node_a->set_default_cursor_shape(Control::CURSOR_ARROW);
+ node_c->set_default_cursor_shape(Control::CURSOR_FORBIDDEN);
+ Window *root = SceneTree::get_singleton()->get_root();
+ DisplayServerMock *DS = (DisplayServerMock *)(DisplayServer::get_singleton());
+
+ // Scene tree:
+ // - root
+ // - node_a (SubViewportContainer)
+ // - node_b (SubViewport)
+ // - node_c (Control)
+
+ root->add_child(node_a);
+ node_a->add_child(node_b);
+ node_b->add_child(node_c);
+
+ Point2i on_c = Point2i(5, 5);
+
+ SEND_GUI_MOUSE_MOTION_EVENT(on_c, MouseButtonMask::NONE, Key::NONE);
+ CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_FORBIDDEN); // GH-74805
+
+ memdelete(node_c);
+ memdelete(node_b);
+ memdelete(node_a);
+ }
+}
+
} // namespace TestViewport
#endif // TEST_VIEWPORT_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/tests/test_main.cpp b/tests/test_main.cpp
index 2e99e6fdb6..96ee146e77 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -31,6 +31,7 @@
#include "test_main.h"
#include "tests/core/config/test_project_settings.h"
+#include "tests/core/input/test_input_event.h"
#include "tests/core/input/test_input_event_key.h"
#include "tests/core/input/test_input_event_mouse.h"
#include "tests/core/input/test_shortcut.h"
diff --git a/thirdparty/README.md b/thirdparty/README.md
index fccb18b1e3..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:
@@ -178,7 +178,7 @@ Files extracted from upstream source:
## freetype
- Upstream: https://www.freetype.org
-- Version: 2.13.0 (de8b92dd7ec634e9e2b25ef534c54a3537555c11, 2023)
+- Version: 2.13.1 (e4586d960f339cf75e2e0b34aee30a0ed8353c0d, 2023)
- License: FreeType License (BSD-like)
Files extracted from upstream source:
@@ -250,7 +250,7 @@ Files extracted from upstream source:
## harfbuzz
- Upstream: https://github.com/harfbuzz/harfbuzz
-- Version: 7.3.0 (4584bcdc326564829d3cee3572386c90e4fd1974, 2023)
+- Version: 8.0.0 (b4305532a7746422e0b615eee6304119c1092fd8, 2023)
- License: MIT
Files extracted from upstream source:
@@ -258,13 +258,13 @@ Files extracted from upstream source:
- `AUTHORS`, `COPYING`, `THANKS`
- from the `src` folder, recursively
- all the `*.c`, `*.cc`, `*.h`, `*.hh` files
- - _except_ `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`
+ - _except_ `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`, `hb-wasm*.*`
## icu4c
- Upstream: https://github.com/unicode-org/icu
-- Version: 73.1 (5861e1fd52f1d7673eee38bc3c965aa18b336062, 2023)
+- Version: 73.2 (680f521746a3bd6a86f25f25ee50a62d88b489cf, 2023)
- License: Unicode
Files extracted from upstream source:
@@ -280,10 +280,11 @@ Files generated from upstream source:
https://github.com/unicode-org/icu/blob/master/docs/userguide/icu_data/buildtool.md
for instructions).
-- Step 1: Build ICU with default options - `./runConfigureICU {PLATFORM} && make`.
-- Step 2: Reconfigure ICU with custom data config - `ICU_DATA_FILTER_FILE={GODOT_SOURCE}/thirdparty/icu4c/godot_data.json ./runConfigureICU {PLATFORM} --with-data-packaging=common`.
-- Step 3: Delete `data/out` folder and rebuild data - `cd data && rm -rf ./out && make`.
-- Step 4: Copy `source/data/out/icudt73l.dat` to the `{GODOT_SOURCE}/thirdparty/icu4c/icudt73l.dat`.
+- Step 1: Download and extract both `icu4c-{version}-src.tgz` and `icu4c-{version}-data.zip` (replace `data` subfolder from the main source archive).
+- Step 2: Build ICU with default options - `./runConfigureICU {PLATFORM} && make`.
+- Step 3: Reconfigure ICU with custom data config - `ICU_DATA_FILTER_FILE={GODOT_SOURCE}/thirdparty/icu4c/godot_data.json ./runConfigureICU {PLATFORM} --with-data-packaging=common`.
+- Step 4: Delete `data/out` folder and rebuild data - `cd data && rm -rf ./out && make`.
+- Step 5: Copy `source/data/out/icudt73l.dat` to the `{GODOT_SOURCE}/thirdparty/icu4c/icudt73l.dat`.
## jpeg-compressor
@@ -582,7 +583,7 @@ Patch files are provided in `oidn/patches/`.
## openxr
- Upstream: https://github.com/KhronosGroup/OpenXR-SDK
-- Version: 1.0.26 (e2da9ce83a4388c9622da328bf48548471261290, 2022)
+- Version: 1.0.28 (f5beb0131f1bea8701ace744d1b50df9049bf331, 2023)
- License: Apache 2.0
Files extracted from upstream source:
@@ -600,7 +601,8 @@ Exclude:
- src/external/android-jni-wrappers and src/external/jnipp (not used yet)
- All CMake stuff: cmake/, CMakeLists.txt and *.cmake
- All Gradle stuff: *gradle*, AndroidManifest.xml
-- All following files (and their .license files): *.{def,in,json,map,pom,rc}
+- All following files (and their .license files): *.{def,expsym,in,json,map,pom,rc,txt}
+- All dotfiles
## pcre2
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;
diff --git a/thirdparty/freetype/include/freetype/config/ftoption.h b/thirdparty/freetype/include/freetype/config/ftoption.h
index 9e03e1783b..1976b33af9 100644
--- a/thirdparty/freetype/include/freetype/config/ftoption.h
+++ b/thirdparty/freetype/include/freetype/config/ftoption.h
@@ -661,36 +661,12 @@ FT_BEGIN_HEADER
* not) instructions in a certain way so that all TrueType fonts look like
* they do in a Windows ClearType (DirectWrite) environment. See [1] for a
* technical overview on what this means. See `ttinterp.h` for more
- * details on the LEAN option.
+ * details on this option.
*
- * There are three possible values.
- *
- * Value 1:
- * This value is associated with the 'Infinality' moniker, contributed by
- * an individual nicknamed Infinality with the goal of making TrueType
- * fonts render better than on Windows. A high amount of configurability
- * and flexibility, down to rules for single glyphs in fonts, but also
- * very slow. Its experimental and slow nature and the original
- * developer losing interest meant that this option was never enabled in
- * default builds.
- *
- * The corresponding interpreter version is v38.
- *
- * Value 2:
- * The new default mode for the TrueType driver. The Infinality code
- * base was stripped to the bare minimum and all configurability removed
- * in the name of speed and simplicity. The configurability was mainly
- * aimed at legacy fonts like 'Arial', 'Times New Roman', or 'Courier'.
- * Legacy fonts are fonts that modify vertical stems to achieve clean
- * black-and-white bitmaps. The new mode focuses on applying a minimal
- * set of rules to all fonts indiscriminately so that modern and web
- * fonts render well while legacy fonts render okay.
- *
- * The corresponding interpreter version is v40.
- *
- * Value 3:
- * Compile both, making both v38 and v40 available (the latter is the
- * default).
+ * The new default mode focuses on applying a minimal set of rules to all
+ * fonts indiscriminately so that modern and web fonts render well while
+ * legacy fonts render okay. The corresponding interpreter version is v40.
+ * The so-called Infinality mode (v38) is no longer available in FreeType.
*
* By undefining these, you get rendering behavior like on Windows without
* ClearType, i.e., Windows XP without ClearType enabled and Win9x
@@ -705,9 +681,7 @@ FT_BEGIN_HEADER
* [1]
* https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx
*/
-/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING 1 */
-#define TT_CONFIG_OPTION_SUBPIXEL_HINTING 2
-/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING ( 1 | 2 ) */
+#define TT_CONFIG_OPTION_SUBPIXEL_HINTING
/**************************************************************************
@@ -977,22 +951,15 @@ FT_BEGIN_HEADER
/*
- * The next three macros are defined if native TrueType hinting is
+ * The next two macros are defined if native TrueType hinting is
* requested by the definitions above. Don't change this.
*/
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
#define TT_USE_BYTECODE_INTERPRETER
-
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
-#if TT_CONFIG_OPTION_SUBPIXEL_HINTING & 1
-#define TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
-#endif
-
-#if TT_CONFIG_OPTION_SUBPIXEL_HINTING & 2
#define TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
#endif
#endif
-#endif
/*
diff --git a/thirdparty/freetype/include/freetype/config/ftstdlib.h b/thirdparty/freetype/include/freetype/config/ftstdlib.h
index 3c9d2ae59a..f65148a902 100644
--- a/thirdparty/freetype/include/freetype/config/ftstdlib.h
+++ b/thirdparty/freetype/include/freetype/config/ftstdlib.h
@@ -111,13 +111,13 @@
#include <stdio.h>
-#define FT_FILE FILE
-#define ft_fclose fclose
-#define ft_fopen fopen
-#define ft_fread fread
-#define ft_fseek fseek
-#define ft_ftell ftell
-#define ft_sprintf sprintf
+#define FT_FILE FILE
+#define ft_fclose fclose
+#define ft_fopen fopen
+#define ft_fread fread
+#define ft_fseek fseek
+#define ft_ftell ftell
+#define ft_snprintf snprintf
/**************************************************************************
diff --git a/thirdparty/freetype/include/freetype/freetype.h b/thirdparty/freetype/include/freetype/freetype.h
index efff74fe39..4a074a4449 100644
--- a/thirdparty/freetype/include/freetype/freetype.h
+++ b/thirdparty/freetype/include/freetype/freetype.h
@@ -102,61 +102,25 @@ FT_BEGIN_HEADER
*/
-
- /*************************************************************************/
- /*************************************************************************/
- /* */
- /* B A S I C T Y P E S */
- /* */
- /*************************************************************************/
- /*************************************************************************/
-
-
/**************************************************************************
*
* @section:
- * base_interface
+ * font_testing_macros
*
* @title:
- * Base Interface
+ * Font Testing Macros
*
* @abstract:
- * The FreeType~2 base font interface.
+ * Macros to test various properties of fonts.
*
* @description:
- * This section describes the most important public high-level API
- * functions of FreeType~2.
+ * Macros to test the most important font properties.
*
- * @order:
- * FT_Library
- * FT_Face
- * FT_Size
- * FT_GlyphSlot
- * FT_CharMap
- * FT_Encoding
- * FT_ENC_TAG
- *
- * FT_FaceRec
- *
- * FT_FACE_FLAG_SCALABLE
- * FT_FACE_FLAG_FIXED_SIZES
- * FT_FACE_FLAG_FIXED_WIDTH
- * FT_FACE_FLAG_HORIZONTAL
- * FT_FACE_FLAG_VERTICAL
- * FT_FACE_FLAG_COLOR
- * FT_FACE_FLAG_SFNT
- * FT_FACE_FLAG_CID_KEYED
- * FT_FACE_FLAG_TRICKY
- * FT_FACE_FLAG_KERNING
- * FT_FACE_FLAG_MULTIPLE_MASTERS
- * FT_FACE_FLAG_VARIATION
- * FT_FACE_FLAG_GLYPH_NAMES
- * FT_FACE_FLAG_EXTERNAL_STREAM
- * FT_FACE_FLAG_HINTER
- * FT_FACE_FLAG_SVG
- * FT_FACE_FLAG_SBIX
- * FT_FACE_FLAG_SBIX_OVERLAY
+ * It is recommended to use these high-level macros instead of directly
+ * testing the corresponding flags, which are scattered over various
+ * structures.
*
+ * @order:
* FT_HAS_HORIZONTAL
* FT_HAS_VERTICAL
* FT_HAS_KERNING
@@ -176,21 +140,59 @@ FT_BEGIN_HEADER
* FT_IS_NAMED_INSTANCE
* FT_IS_VARIATION
*
- * FT_STYLE_FLAG_BOLD
- * FT_STYLE_FLAG_ITALIC
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * library_setup
*
- * FT_SizeRec
- * FT_Size_Metrics
+ * @title:
+ * Library Setup
*
- * FT_GlyphSlotRec
- * FT_Glyph_Metrics
- * FT_SubGlyph
+ * @abstract:
+ * Functions to start and end the usage of the FreeType library.
*
- * FT_Bitmap_Size
+ * @description:
+ * Functions to start and end the usage of the FreeType library.
+ *
+ * Note that @FT_Library_Version and @FREETYPE_XXX are of limited use
+ * because even a new release of FreeType with only documentation
+ * changes increases the version number.
*
+ * @order:
+ * FT_Library
* FT_Init_FreeType
* FT_Done_FreeType
*
+ * FT_Library_Version
+ * FREETYPE_XXX
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * face_creation
+ *
+ * @title:
+ * Face Creation
+ *
+ * @abstract:
+ * Functions to manage fonts.
+ *
+ * @description:
+ * The functions and structures collected in this section operate on
+ * fonts globally.
+ *
+ * @order:
+ * FT_Face
+ * FT_FaceRec
+ * FT_FACE_FLAG_XXX
+ * FT_STYLE_FLAG_XXX
+ *
* FT_New_Face
* FT_Done_Face
* FT_Reference_Face
@@ -198,10 +200,36 @@ FT_BEGIN_HEADER
* FT_Face_Properties
* FT_Open_Face
* FT_Open_Args
+ * FT_OPEN_XXX
* FT_Parameter
* FT_Attach_File
* FT_Attach_Stream
*
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * sizing_and_scaling
+ *
+ * @title:
+ * Sizing and Scaling
+ *
+ * @abstract:
+ * Functions to manage font sizes.
+ *
+ * @description:
+ * The functions and structures collected in this section are related to
+ * selecting and manipulating the size of a font globally.
+ *
+ * @order:
+ * FT_Size
+ * FT_SizeRec
+ * FT_Size_Metrics
+ *
+ * FT_Bitmap_Size
+ *
* FT_Set_Char_Size
* FT_Set_Pixel_Sizes
* FT_Request_Size
@@ -209,44 +237,37 @@ FT_BEGIN_HEADER
* FT_Size_Request_Type
* FT_Size_RequestRec
* FT_Size_Request
+ *
* FT_Set_Transform
* FT_Get_Transform
- * FT_Load_Glyph
- * FT_Get_Char_Index
- * FT_Get_First_Char
- * FT_Get_Next_Char
- * FT_Load_Char
*
- * FT_OPEN_MEMORY
- * FT_OPEN_STREAM
- * FT_OPEN_PATHNAME
- * FT_OPEN_DRIVER
- * FT_OPEN_PARAMS
- *
- * FT_LOAD_DEFAULT
- * FT_LOAD_RENDER
- * FT_LOAD_MONOCHROME
- * FT_LOAD_LINEAR_DESIGN
- * FT_LOAD_NO_SCALE
- * FT_LOAD_NO_HINTING
- * FT_LOAD_NO_BITMAP
- * FT_LOAD_SBITS_ONLY
- * FT_LOAD_NO_AUTOHINT
- * FT_LOAD_COLOR
- *
- * FT_LOAD_VERTICAL_LAYOUT
- * FT_LOAD_IGNORE_TRANSFORM
- * FT_LOAD_FORCE_AUTOHINT
- * FT_LOAD_NO_RECURSE
- * FT_LOAD_PEDANTIC
- *
- * FT_LOAD_TARGET_NORMAL
- * FT_LOAD_TARGET_LIGHT
- * FT_LOAD_TARGET_MONO
- * FT_LOAD_TARGET_LCD
- * FT_LOAD_TARGET_LCD_V
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * glyph_retrieval
+ *
+ * @title:
+ * Glyph Retrieval
+ *
+ * @abstract:
+ * Functions to manage glyphs.
+ *
+ * @description:
+ * The functions and structures collected in this section operate on
+ * single glyphs, of which @FT_Load_Glyph is most important.
*
+ * @order:
+ * FT_GlyphSlot
+ * FT_GlyphSlotRec
+ * FT_Glyph_Metrics
+ *
+ * FT_Load_Glyph
+ * FT_LOAD_XXX
* FT_LOAD_TARGET_MODE
+ * FT_LOAD_TARGET_XXX
*
* FT_Render_Glyph
* FT_Render_Mode
@@ -254,34 +275,121 @@ FT_BEGIN_HEADER
* FT_Kerning_Mode
* FT_Get_Track_Kerning
*
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * character_mapping
+ *
+ * @title:
+ * Character Mapping
+ *
+ * @abstract:
+ * Functions to manage character-to-glyph maps.
+ *
+ * @description:
+ * This section holds functions and structures that are related to
+ * mapping character input codes to glyph indices.
+ *
+ * Note that for many scripts the simplistic approach used by FreeType
+ * of mapping a single character to a single glyph is not valid or
+ * possible! In general, a higher-level library like HarfBuzz or ICU
+ * should be used for handling text strings.
+ *
+ * @order:
+ * FT_CharMap
* FT_CharMapRec
+ * FT_Encoding
+ * FT_ENC_TAG
+ *
* FT_Select_Charmap
* FT_Set_Charmap
* FT_Get_Charmap_Index
*
+ * FT_Get_Char_Index
+ * FT_Get_First_Char
+ * FT_Get_Next_Char
+ * FT_Load_Char
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * information_retrieval
+ *
+ * @title:
+ * Information Retrieval
+ *
+ * @abstract:
+ * Functions to retrieve font and glyph information.
+ *
+ * @description:
+ * Functions to retrieve font and glyph information. Only some very
+ * basic data is covered; see also the chapter on the format-specific
+ * API for more.
+ *
+ *
+ * @order:
* FT_Get_Name_Index
* FT_Get_Glyph_Name
* FT_Get_Postscript_Name
* FT_Get_FSType_Flags
+ * FT_FSTYPE_XXX
* FT_Get_SubGlyph_Info
+ * FT_SUBGLYPH_FLAG_XXX
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * other_api_data
+ *
+ * @title:
+ * Other API Data
+ *
+ * @abstract:
+ * Other structures, enumerations, and macros.
*
+ * @description:
+ * Other structures, enumerations, and macros. Deprecated functions are
+ * also listed here.
+ *
+ * @order:
* FT_Face_Internal
* FT_Size_Internal
* FT_Slot_Internal
*
- * FT_FACE_FLAG_XXX
- * FT_STYLE_FLAG_XXX
- * FT_OPEN_XXX
- * FT_LOAD_XXX
- * FT_LOAD_TARGET_XXX
- * FT_SUBGLYPH_FLAG_XXX
- * FT_FSTYPE_XXX
+ * FT_SubGlyph
*
* FT_HAS_FAST_GLYPHS
+ * FT_Face_CheckTrueTypePatents
+ * FT_Face_SetUnpatentedHinting
*
*/
+ /*************************************************************************/
+ /*************************************************************************/
+ /* */
+ /* B A S I C T Y P E S */
+ /* */
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /**************************************************************************
+ *
+ * @section:
+ * glyph_retrieval
+ *
+ */
+
/**************************************************************************
*
* @struct:
@@ -351,6 +459,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * sizing_and_scaling
+ *
+ */
+
+ /**************************************************************************
+ *
* @struct:
* FT_Bitmap_Size
*
@@ -411,6 +526,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * library_setup
+ *
+ */
+
+ /**************************************************************************
+ *
* @type:
* FT_Library
*
@@ -483,7 +605,7 @@ FT_BEGIN_HEADER
/**************************************************************************
*
* @section:
- * base_interface
+ * face_creation
*
*/
@@ -521,6 +643,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * sizing_and_scaling
+ *
+ */
+
+ /**************************************************************************
+ *
* @type:
* FT_Size
*
@@ -553,6 +682,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * glyph_retrieval
+ *
+ */
+
+ /**************************************************************************
+ *
* @type:
* FT_GlyphSlot
*
@@ -572,6 +708,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * character_mapping
+ *
+ */
+
+ /**************************************************************************
+ *
* @type:
* FT_CharMap
*
@@ -879,6 +1022,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * other_api_data
+ *
+ */
+
+ /**************************************************************************
+ *
* @type:
* FT_Face_Internal
*
@@ -894,6 +1044,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * face_creation
+ *
+ */
+
+ /**************************************************************************
+ *
* @struct:
* FT_FaceRec
*
@@ -918,7 +1075,7 @@ FT_BEGIN_HEADER
* If we have the third named instance of face~4, say, `face_index` is
* set to 0x00030004.
*
- * Bit 31 is always zero (this is, `face_index` is always a positive
+ * Bit 31 is always zero (that is, `face_index` is always a positive
* value).
*
* [Since 2.9] Changing the design coordinates with
@@ -936,7 +1093,7 @@ FT_BEGIN_HEADER
*
* [Since 2.6.1] Bits 16-30 hold the number of named instances
* available for the current face if we have a GX or OpenType variation
- * (sub)font. Bit 31 is always zero (this is, `style_flags` is always
+ * (sub)font. Bit 31 is always zero (that is, `style_flags` is always
* a positive value). Note that a variation font has always at least
* one named instance, namely the default instance.
*
@@ -1002,7 +1159,7 @@ FT_BEGIN_HEADER
* Note that the bounding box might be off by (at least) one pixel for
* hinted fonts. See @FT_Size_Metrics for further discussion.
*
- * Note that the bounding box does not vary in OpenType variable fonts
+ * Note that the bounding box does not vary in OpenType variation fonts
* and should only be used in relation to the default instance.
*
* units_per_EM ::
@@ -1090,9 +1247,9 @@ FT_BEGIN_HEADER
FT_Generic generic;
- /*# The following member variables (down to `underline_thickness`) */
- /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size */
- /*# for bitmap fonts. */
+ /* The following member variables (down to `underline_thickness`) */
+ /* are only relevant to scalable outlines; cf. @FT_Bitmap_Size */
+ /* for bitmap fonts. */
FT_BBox bbox;
FT_UShort units_per_EM;
@@ -1110,7 +1267,7 @@ FT_BEGIN_HEADER
FT_Size size;
FT_CharMap charmap;
- /*@private begin */
+ /* private fields, internal to FreeType */
FT_Driver driver;
FT_Memory memory;
@@ -1123,8 +1280,6 @@ FT_BEGIN_HEADER
FT_Face_Internal internal;
- /*@private end */
-
} FT_FaceRec;
@@ -1207,13 +1362,13 @@ FT_BEGIN_HEADER
* successfully; in all other cases you get an
* `FT_Err_Invalid_Argument` error.
*
- * Note that CID-keyed fonts that are in an SFNT wrapper (this is, all
+ * Note that CID-keyed fonts that are in an SFNT wrapper (that is, all
* OpenType/CFF fonts) don't have this flag set since the glyphs are
* accessed in the normal way (using contiguous indices); the
* 'CID-ness' isn't visible to the application.
*
* FT_FACE_FLAG_TRICKY ::
- * The face is 'tricky', this is, it always needs the font format's
+ * The face is 'tricky', that is, it always needs the font format's
* native hinting engine to get a reasonable result. A typical example
* is the old Chinese font `mingli.ttf` (but not `mingliu.ttc`) that
* uses TrueType bytecode instructions to move and scale all of its
@@ -1235,8 +1390,8 @@ FT_BEGIN_HEADER
* FT_FACE_FLAG_VARIATION ::
* [Since 2.9] Set if the current face (or named instance) has been
* altered with @FT_Set_MM_Design_Coordinates,
- * @FT_Set_Var_Design_Coordinates, or @FT_Set_Var_Blend_Coordinates.
- * This flag is unset by a call to @FT_Set_Named_Instance.
+ * @FT_Set_Var_Design_Coordinates, @FT_Set_Var_Blend_Coordinates, or
+ * @FT_Set_MM_WeightVector to select a non-default instance.
*
* FT_FACE_FLAG_SVG ::
* [Since 2.12] The face has an 'SVG~' OpenType table.
@@ -1274,6 +1429,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * font_testing_macros
+ *
+ */
+
+ /**************************************************************************
+ *
* @macro:
* FT_HAS_HORIZONTAL
*
@@ -1383,6 +1545,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * other_api_data
+ *
+ */
+
+ /**************************************************************************
+ *
* @macro:
* FT_HAS_FAST_GLYPHS
*
@@ -1395,6 +1564,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * font_testing_macros
+ *
+ */
+
+ /**************************************************************************
+ *
* @macro:
* FT_HAS_GLYPH_NAMES
*
@@ -1451,8 +1627,8 @@ FT_BEGIN_HEADER
*
* @description:
* A macro that returns true whenever a face object has been altered by
- * @FT_Set_MM_Design_Coordinates, @FT_Set_Var_Design_Coordinates, or
- * @FT_Set_Var_Blend_Coordinates.
+ * @FT_Set_MM_Design_Coordinates, @FT_Set_Var_Design_Coordinates,
+ * @FT_Set_Var_Blend_Coordinates, or @FT_Set_MM_WeightVector.
*
* @since:
* 2.9
@@ -1630,6 +1806,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * face_creation
+ *
+ */
+
+ /**************************************************************************
+ *
* @enum:
* FT_STYLE_FLAG_XXX
*
@@ -1656,6 +1839,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * other_api_data
+ *
+ */
+
+ /**************************************************************************
+ *
* @type:
* FT_Size_Internal
*
@@ -1668,6 +1858,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * sizing_and_scaling
+ *
+ */
+
+ /**************************************************************************
+ *
* @struct:
* FT_Size_Metrics
*
@@ -1819,6 +2016,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * other_api_data
+ *
+ */
+
+ /**************************************************************************
+ *
* @struct:
* FT_SubGlyph
*
@@ -1850,6 +2054,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * glyph_retrieval
+ *
+ */
+
+ /**************************************************************************
+ *
* @struct:
* FT_GlyphSlotRec
*
@@ -2094,6 +2305,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * library_setup
+ *
+ */
+
+ /**************************************************************************
+ *
* @function:
* FT_Init_FreeType
*
@@ -2151,6 +2369,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * face_creation
+ *
+ */
+
+ /**************************************************************************
+ *
* @enum:
* FT_OPEN_XXX
*
@@ -2451,7 +2676,7 @@ FT_BEGIN_HEADER
* Each new face object created with this function also owns a default
* @FT_Size object, accessible as `face->size`.
*
- * One @FT_Library instance can have multiple face objects, this is,
+ * One @FT_Library instance can have multiple face objects, that is,
* @FT_Open_Face and its siblings can be called multiple times using the
* same `library` argument.
*
@@ -2652,6 +2877,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * sizing_and_scaling
+ *
+ */
+
+ /**************************************************************************
+ *
* @function:
* FT_Select_Size
*
@@ -2679,7 +2911,7 @@ FT_BEGIN_HEADER
* silently uses outlines if there is no bitmap for a given glyph index.
*
* For GX and OpenType variation fonts, a bitmap strike makes sense only
- * if the default instance is active (this is, no glyph variation takes
+ * if the default instance is active (that is, no glyph variation takes
* place); otherwise, FreeType simply ignores bitmap strikes. The same
* is true for all named instances that are different from the default
* instance.
@@ -2944,6 +3176,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * glyph_retrieval
+ *
+ */
+
+ /**************************************************************************
+ *
* @function:
* FT_Load_Glyph
*
@@ -2976,7 +3215,7 @@ FT_BEGIN_HEADER
* glyph may be transformed. See @FT_Set_Transform for the details.
*
* For subsetted CID-keyed fonts, `FT_Err_Invalid_Argument` is returned
- * for invalid CID values (this is, for CID values that don't have a
+ * for invalid CID values (that is, for CID values that don't have a
* corresponding glyph in the font). See the discussion of the
* @FT_FACE_FLAG_CID_KEYED flag for more details.
*
@@ -2992,6 +3231,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * character_mapping
+ *
+ */
+
+ /**************************************************************************
+ *
* @function:
* FT_Load_Char
*
@@ -3035,6 +3281,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * glyph_retrieval
+ *
+ */
+
+ /**************************************************************************
+ *
* @enum:
* FT_LOAD_XXX
*
@@ -3172,10 +3425,11 @@ FT_BEGIN_HEADER
*
* [Since 2.12] If the glyph index maps to an entry in the face's
* 'SVG~' table, load the associated SVG document from this table and
- * set the `format` field of @FT_GlyphSlotRec to @FT_GLYPH_FORMAT_SVG.
- * Note that FreeType itself can't render SVG documents; however, the
- * library provides hooks to seamlessly integrate an external renderer.
- * See sections @ot_svg_driver and @svg_fonts for more.
+ * set the `format` field of @FT_GlyphSlotRec to @FT_GLYPH_FORMAT_SVG
+ * ([since 2.13.1] provided @FT_LOAD_NO_SVG is not set). Note that
+ * FreeType itself can't render SVG documents; however, the library
+ * provides hooks to seamlessly integrate an external renderer. See
+ * sections @ot_svg_driver and @svg_fonts for more.
*
* [Since 2.10, experimental] If the glyph index maps to an entry in
* the face's 'COLR' table with a 'CPAL' palette table (as defined in
@@ -3189,6 +3443,9 @@ FT_BEGIN_HEADER
* @FT_Palette_Select instead of setting @FT_LOAD_COLOR for rendering
* so that the client application can handle blending by itself.
*
+ * FT_LOAD_NO_SVG ::
+ * [Since 2.13.1] Ignore SVG glyph data when loading.
+ *
* FT_LOAD_COMPUTE_METRICS ::
* [Since 2.6.1] Compute glyph metrics from the glyph data, without the
* use of bundled metrics tables (for example, the 'hdmx' table in
@@ -3254,6 +3511,7 @@ FT_BEGIN_HEADER
#define FT_LOAD_COLOR ( 1L << 20 )
#define FT_LOAD_COMPUTE_METRICS ( 1L << 21 )
#define FT_LOAD_BITMAP_METRICS_ONLY ( 1L << 22 )
+#define FT_LOAD_NO_SVG ( 1L << 24 )
/* */
@@ -3374,6 +3632,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * sizing_and_scaling
+ *
+ */
+
+ /**************************************************************************
+ *
* @function:
* FT_Set_Transform
*
@@ -3449,6 +3714,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * glyph_retrieval
+ *
+ */
+
+ /**************************************************************************
+ *
* @enum:
* FT_Render_Mode
*
@@ -3843,6 +4115,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * character_mapping
+ *
+ */
+
+ /**************************************************************************
+ *
* @function:
* FT_Select_Charmap
*
@@ -4059,6 +4338,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * face_creation
+ *
+ */
+
+ /**************************************************************************
+ *
* @function:
* FT_Face_Properties
*
@@ -4157,6 +4443,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * information_retrieval
+ *
+ */
+
+ /**************************************************************************
+ *
* @function:
* FT_Get_Name_Index
*
@@ -4266,9 +4559,10 @@ FT_BEGIN_HEADER
*
* [Since 2.9] Special PostScript names for named instances are only
* returned if the named instance is set with @FT_Set_Named_Instance (and
- * the font has corresponding entries in its 'fvar' table). If
- * @FT_IS_VARIATION returns true, the algorithmically derived PostScript
- * name is provided, not looking up special entries for named instances.
+ * the font has corresponding entries in its 'fvar' table or is the
+ * default named instance). If @FT_IS_VARIATION returns true, the
+ * algorithmically derived PostScript name is provided, not looking up
+ * special entries for named instances.
*/
FT_EXPORT( const char* )
FT_Get_Postscript_Name( FT_Face face );
@@ -4900,32 +5194,10 @@ FT_BEGIN_HEADER
/**************************************************************************
*
* @section:
- * version
- *
- * @title:
- * FreeType Version
- *
- * @abstract:
- * Functions and macros related to FreeType versions.
- *
- * @description:
- * Note that those functions and macros are of limited use because even a
- * new release of FreeType with only documentation changes increases the
- * version number.
- *
- * @order:
- * FT_Library_Version
- *
- * FREETYPE_MAJOR
- * FREETYPE_MINOR
- * FREETYPE_PATCH
- *
- * FT_Face_CheckTrueTypePatents
- * FT_Face_SetUnpatentedHinting
+ * library_setup
*
*/
-
/**************************************************************************
*
* @enum:
@@ -4950,7 +5222,7 @@ FT_BEGIN_HEADER
*/
#define FREETYPE_MAJOR 2
#define FREETYPE_MINOR 13
-#define FREETYPE_PATCH 0
+#define FREETYPE_PATCH 1
/**************************************************************************
@@ -4994,6 +5266,13 @@ FT_BEGIN_HEADER
/**************************************************************************
*
+ * @section:
+ * other_api_data
+ *
+ */
+
+ /**************************************************************************
+ *
* @function:
* FT_Face_CheckTrueTypePatents
*
diff --git a/thirdparty/freetype/include/freetype/ftcache.h b/thirdparty/freetype/include/freetype/ftcache.h
index c76869545a..a2072e26b8 100644
--- a/thirdparty/freetype/include/freetype/ftcache.h
+++ b/thirdparty/freetype/include/freetype/ftcache.h
@@ -43,61 +43,61 @@ FT_BEGIN_HEADER
* objects, as well as caching information like character maps and glyph
* images while limiting their maximum memory usage.
*
- * Note that all types and functions begin with the `FTC_` prefix.
- *
- * The cache is highly portable and thus doesn't know anything about the
- * fonts installed on your system, or how to access them. This implies
- * the following scheme:
- *
- * First, available or installed font faces are uniquely identified by
- * @FTC_FaceID values, provided to the cache by the client. Note that
- * the cache only stores and compares these values, and doesn't try to
- * interpret them in any way.
- *
- * Second, the cache calls, only when needed, a client-provided function
- * to convert an @FTC_FaceID into a new @FT_Face object. The latter is
- * then completely managed by the cache, including its termination
- * through @FT_Done_Face. To monitor termination of face objects, the
- * finalizer callback in the `generic` field of the @FT_Face object can
- * be used, which might also be used to store the @FTC_FaceID of the
- * face.
- *
- * Clients are free to map face IDs to anything else. The most simple
- * usage is to associate them to a (pathname,face_index) pair that is
- * used to call @FT_New_Face. However, more complex schemes are also
- * possible.
+ * Note that all types and functions begin with the `FTC_` prefix rather
+ * than the usual `FT_` prefix in the rest of FreeType.
+ *
+ * The cache is highly portable and, thus, doesn't know anything about
+ * the fonts installed on your system, or how to access them. Therefore,
+ * it requires the following.
+ *
+ * * @FTC_FaceID, an arbitrary non-zero value that uniquely identifies
+ * available or installed font faces, has to be provided to the
+ * cache by the client. Note that the cache only stores and compares
+ * these values and doesn't try to interpret them in any way, but they
+ * have to be persistent on the client side.
+ *
+ * * @FTC_Face_Requester, a method to convert an @FTC_FaceID into a new
+ * @FT_Face object when necessary, has to be provided to the cache by
+ * the client. The @FT_Face object is completely managed by the cache,
+ * including its termination through @FT_Done_Face. To monitor
+ * termination of face objects, the finalizer callback in the `generic`
+ * field of the @FT_Face object can be used, which might also be used
+ * to store the @FTC_FaceID of the face.
+ *
+ * Clients are free to map face IDs to anything useful. The most simple
+ * usage is, for example, to associate them to a `{pathname,face_index}`
+ * pair that is then used by @FTC_Face_Requester to call @FT_New_Face.
+ * However, more complex schemes are also possible.
*
* Note that for the cache to work correctly, the face ID values must be
* **persistent**, which means that the contents they point to should not
* change at runtime, or that their value should not become invalid.
- *
* If this is unavoidable (e.g., when a font is uninstalled at runtime),
- * you should call @FTC_Manager_RemoveFaceID as soon as possible, to let
+ * you should call @FTC_Manager_RemoveFaceID as soon as possible to let
* the cache get rid of any references to the old @FTC_FaceID it may keep
* internally. Failure to do so will lead to incorrect behaviour or even
- * crashes.
+ * crashes in @FTC_Face_Requester.
*
* To use the cache, start with calling @FTC_Manager_New to create a new
* @FTC_Manager object, which models a single cache instance. You can
* then look up @FT_Face and @FT_Size objects with
- * @FTC_Manager_LookupFace and @FTC_Manager_LookupSize, respectively.
- *
- * If you want to use the charmap caching, call @FTC_CMapCache_New, then
- * later use @FTC_CMapCache_Lookup to perform the equivalent of
- * @FT_Get_Char_Index, only much faster.
- *
- * If you want to use the @FT_Glyph caching, call @FTC_ImageCache_New,
- * then later use @FTC_ImageCache_Lookup to retrieve the corresponding
- * @FT_Glyph objects from the cache.
- *
- * If you need lots of small bitmaps, it is much more memory efficient to
- * call @FTC_SBitCache_New followed by @FTC_SBitCache_Lookup. This
- * returns @FTC_SBitRec structures, which are used to store small bitmaps
- * directly. (A small bitmap is one whose metrics and dimensions all fit
- * into 8-bit integers).
- *
- * We hope to also provide a kerning cache in the near future.
- *
+ * @FTC_Manager_LookupFace and @FTC_Manager_LookupSize, respectively, and
+ * use them in any FreeType work stream. You can also cache other
+ * FreeType objects as follows.
+ *
+ * * If you want to use the charmap caching, call @FTC_CMapCache_New,
+ * then later use @FTC_CMapCache_Lookup to perform the equivalent of
+ * @FT_Get_Char_Index, only much faster.
+ *
+ * * If you want to use the @FT_Glyph caching, call @FTC_ImageCache_New,
+ * then later use @FTC_ImageCache_Lookup to retrieve the corresponding
+ * @FT_Glyph objects from the cache.
+ *
+ * * If you need lots of small bitmaps, it is much more memory-efficient
+ * to call @FTC_SBitCache_New followed by @FTC_SBitCache_Lookup. This
+ * returns @FTC_SBitRec structures, which are used to store small
+ * bitmaps directly. (A small bitmap is one whose metrics and
+ * dimensions all fit into 8-bit integers).
*
* @order:
* FTC_Manager
diff --git a/thirdparty/freetype/include/freetype/ftchapters.h b/thirdparty/freetype/include/freetype/ftchapters.h
index 6a9733ad7c..7566fbd10f 100644
--- a/thirdparty/freetype/include/freetype/ftchapters.h
+++ b/thirdparty/freetype/include/freetype/ftchapters.h
@@ -31,9 +31,28 @@
* Core API
*
* @sections:
- * version
* basic_types
- * base_interface
+ * library_setup
+ * face_creation
+ * font_testing_macros
+ * sizing_and_scaling
+ * glyph_retrieval
+ * character_mapping
+ * information_retrieval
+ * other_api_data
+ *
+ */
+
+
+ /**************************************************************************
+ *
+ * @chapter:
+ * extended_api
+ *
+ * @title:
+ * Extended API
+ *
+ * @sections:
* glyph_variants
* color_management
* layer_management
diff --git a/thirdparty/freetype/include/freetype/ftdriver.h b/thirdparty/freetype/include/freetype/ftdriver.h
index f90946fd17..7af7465bc7 100644
--- a/thirdparty/freetype/include/freetype/ftdriver.h
+++ b/thirdparty/freetype/include/freetype/ftdriver.h
@@ -134,7 +134,7 @@ FT_BEGIN_HEADER
* each being rounded to the nearest pixel edge, taking care of overshoot
* suppression at small sizes, stem darkening, and scaling.
*
- * Hstems (this is, hint values defined in the font to help align
+ * Hstems (that is, hint values defined in the font to help align
* horizontal features) that fall within a blue zone are said to be
* 'captured' and are aligned to that zone. Uncaptured stems are moved
* in one of four ways, top edge up or down, bottom edge up or down.
@@ -446,7 +446,7 @@ FT_BEGIN_HEADER
* at smaller sizes.
*
* For the auto-hinter, stem-darkening is experimental currently and thus
- * switched off by default (this is, `no-stem-darkening` is set to TRUE
+ * switched off by default (that is, `no-stem-darkening` is set to TRUE
* by default). Total consistency with the CFF driver is not achieved
* right now because the emboldening method differs and glyphs must be
* scaled down on the Y-axis to keep outline points inside their
@@ -651,11 +651,8 @@ FT_BEGIN_HEADER
* Windows~98; only grayscale and B/W rasterizing is supported.
*
* TT_INTERPRETER_VERSION_38 ::
- * Version~38 corresponds to MS rasterizer v.1.9; it is roughly
- * equivalent to the hinting provided by DirectWrite ClearType (as can
- * be found, for example, in the Internet Explorer~9 running on
- * Windows~7). It is used in FreeType to select the 'Infinality'
- * subpixel hinting code. The code may be removed in a future version.
+ * Version~38 is the same Version~40. The original 'Infinality' code is
+ * no longer available.
*
* TT_INTERPRETER_VERSION_40 ::
* Version~40 corresponds to MS rasterizer v.2.1; it is roughly
diff --git a/thirdparty/freetype/include/freetype/ftimage.h b/thirdparty/freetype/include/freetype/ftimage.h
index 2e8e6734cc..6baa812560 100644
--- a/thirdparty/freetype/include/freetype/ftimage.h
+++ b/thirdparty/freetype/include/freetype/ftimage.h
@@ -19,7 +19,7 @@
/**************************************************************************
*
* Note: A 'raster' is simply a scan-line converter, used to render
- * FT_Outlines into FT_Bitmaps.
+ * `FT_Outline`s into `FT_Bitmap`s.
*
*/
@@ -256,6 +256,12 @@ FT_BEGIN_HEADER
* palette ::
* A typeless pointer to the bitmap palette; this field is intended for
* paletted pixel modes. Not used currently.
+ *
+ * @note:
+ * `width` and `rows` refer to the *physical* size of the bitmap, not the
+ * *logical* one. For example, if @FT_Pixel_Mode is set to
+ * `FT_PIXEL_MODE_LCD`, the logical width is a just a third of the
+ * physical one.
*/
typedef struct FT_Bitmap_
{
@@ -856,7 +862,7 @@ FT_BEGIN_HEADER
* @FT_SpanFunc that takes the y~coordinate of the span as a parameter.
*
* The anti-aliased rasterizer produces coverage values from 0 to 255,
- * this is, from completely transparent to completely opaque.
+ * that is, from completely transparent to completely opaque.
*/
typedef struct FT_Span_
{
diff --git a/thirdparty/freetype/include/freetype/ftlogging.h b/thirdparty/freetype/include/freetype/ftlogging.h
index 2246dc8365..53b8b89642 100644
--- a/thirdparty/freetype/include/freetype/ftlogging.h
+++ b/thirdparty/freetype/include/freetype/ftlogging.h
@@ -62,7 +62,7 @@ FT_BEGIN_HEADER
* component.
*
* ```
- * FT_Trace_Set_Level( "any:7 memory:0 );
+ * FT_Trace_Set_Level( "any:7 memory:0" );
* ```
*
* @note:
diff --git a/thirdparty/freetype/include/freetype/ftmm.h b/thirdparty/freetype/include/freetype/ftmm.h
index e381ef3d30..d145128a9b 100644
--- a/thirdparty/freetype/include/freetype/ftmm.h
+++ b/thirdparty/freetype/include/freetype/ftmm.h
@@ -153,7 +153,7 @@ FT_BEGIN_HEADER
* @note:
* The fields `minimum`, `def`, and `maximum` are 16.16 fractional values
* for TrueType GX and OpenType variation fonts. For Adobe MM fonts, the
- * values are integers.
+ * values are whole numbers (i.e., the fractional part is zero).
*/
typedef struct FT_Var_Axis_
{
@@ -399,8 +399,8 @@ FT_BEGIN_HEADER
*
* @note:
* The design coordinates are 16.16 fractional values for TrueType GX and
- * OpenType variation fonts. For Adobe MM fonts, the values are
- * integers.
+ * OpenType variation fonts. For Adobe MM fonts, the values are supposed
+ * to be whole numbers (i.e., the fractional part is zero).
*
* [Since 2.8.1] To reset all axes to the default values, call the
* function with `num_coords` set to zero and `coords` set to `NULL`.
@@ -446,8 +446,8 @@ FT_BEGIN_HEADER
*
* @note:
* The design coordinates are 16.16 fractional values for TrueType GX and
- * OpenType variation fonts. For Adobe MM fonts, the values are
- * integers.
+ * OpenType variation fonts. For Adobe MM fonts, the values are whole
+ * numbers (i.e., the fractional part is zero).
*
* @since:
* 2.7.1
@@ -602,10 +602,12 @@ FT_BEGIN_HEADER
*
* @note:
* Adobe Multiple Master fonts limit the number of designs, and thus the
- * length of the weight vector to~16.
+ * length of the weight vector to 16~elements.
*
- * If `len` is zero and `weightvector` is `NULL`, the weight vector array
- * is reset to the default values.
+ * If `len` is larger than zero, this function sets the
+ * @FT_FACE_FLAG_VARIATION bit in @FT_Face's `face_flags` field (i.e.,
+ * @FT_IS_VARIATION will return true). If `len` is zero, this bit flag
+ * is unset and the weight vector array is reset to the default values.
*
* The Adobe documentation also states that the values in the
* WeightVector array must total 1.0 +/-~0.001. In practice this does
@@ -753,6 +755,45 @@ FT_BEGIN_HEADER
FT_Set_Named_Instance( FT_Face face,
FT_UInt instance_index );
+
+ /**************************************************************************
+ *
+ * @function:
+ * FT_Get_Default_Named_Instance
+ *
+ * @description:
+ * Retrieve the index of the default named instance, to be used with
+ * @FT_Set_Named_Instance.
+ *
+ * The default instance of a variation font is that instance for which
+ * the nth axis coordinate is equal to `axis[n].def` (as specified in the
+ * @FT_MM_Var structure), with~n covering all axes.
+ *
+ * FreeType synthesizes a named instance for the default instance if the
+ * font does not contain such an entry.
+ *
+ * @input:
+ * face ::
+ * A handle to the source face.
+ *
+ * @output:
+ * instance_index ::
+ * The index of the default named instance.
+ *
+ * @return:
+ * FreeType error code. 0~means success.
+ *
+ * @note:
+ * For Adobe MM fonts (which don't have named instances) this function
+ * always returns zero for `instance_index`.
+ *
+ * @since:
+ * 2.13.1
+ */
+ FT_EXPORT( FT_Error )
+ FT_Get_Default_Named_Instance( FT_Face face,
+ FT_UInt *instance_index );
+
/* */
diff --git a/thirdparty/freetype/include/freetype/ftoutln.h b/thirdparty/freetype/include/freetype/ftoutln.h
index 54434b25f6..f9329ca40c 100644
--- a/thirdparty/freetype/include/freetype/ftoutln.h
+++ b/thirdparty/freetype/include/freetype/ftoutln.h
@@ -118,7 +118,7 @@ FT_BEGIN_HEADER
* attachement.
*
* Similarly, the function returns success for an empty outline also
- * (doing nothing, this is, not calling any emitter); if necessary, you
+ * (doing nothing, that is, not calling any emitter); if necessary, you
* should filter this out, too.
*/
FT_EXPORT( FT_Error )
diff --git a/thirdparty/freetype/include/freetype/ftrender.h b/thirdparty/freetype/include/freetype/ftrender.h
index a8576dab00..0b6fad32e8 100644
--- a/thirdparty/freetype/include/freetype/ftrender.h
+++ b/thirdparty/freetype/include/freetype/ftrender.h
@@ -158,7 +158,7 @@ FT_BEGIN_HEADER
FT_Renderer_GetCBoxFunc get_glyph_cbox;
FT_Renderer_SetModeFunc set_mode;
- FT_Raster_Funcs* raster_class;
+ const FT_Raster_Funcs* raster_class;
} FT_Renderer_Class;
diff --git a/thirdparty/freetype/include/freetype/ftsynth.h b/thirdparty/freetype/include/freetype/ftsynth.h
index 5d19697657..af90967dda 100644
--- a/thirdparty/freetype/include/freetype/ftsynth.h
+++ b/thirdparty/freetype/include/freetype/ftsynth.h
@@ -68,6 +68,18 @@ FT_BEGIN_HEADER
FT_EXPORT( void )
FT_GlyphSlot_Embolden( FT_GlyphSlot slot );
+ /* Precisely adjust the glyph weight either horizontally or vertically. */
+ /* The `xdelta` and `ydelta` values are fractions of the face Em size */
+ /* (in fixed-point format). Considering that a regular face would have */
+ /* stem widths on the order of 0.1 Em, a delta of 0.05 (0x0CCC) should */
+ /* be very noticeable. To increase or decrease the weight, use positive */
+ /* or negative values, respectively. */
+ FT_EXPORT( void )
+ FT_GlyphSlot_AdjustWeight( FT_GlyphSlot slot,
+ FT_Fixed xdelta,
+ FT_Fixed ydelta );
+
+
/* Slant an outline glyph to the right by about 12 degrees. */
FT_EXPORT( void )
FT_GlyphSlot_Oblique( FT_GlyphSlot slot );
diff --git a/thirdparty/freetype/include/freetype/ftsystem.h b/thirdparty/freetype/include/freetype/ftsystem.h
index a995b078de..3a08f4912c 100644
--- a/thirdparty/freetype/include/freetype/ftsystem.h
+++ b/thirdparty/freetype/include/freetype/ftsystem.h
@@ -229,8 +229,7 @@ FT_BEGIN_HEADER
* A handle to the source stream.
*
* offset ::
- * The offset from the start of the stream to seek to if this is a seek
- * operation (see note).
+ * The offset from the start of the stream to seek to.
*
* buffer ::
* The address of the read buffer.
@@ -239,16 +238,9 @@ FT_BEGIN_HEADER
* The number of bytes to read from the stream.
*
* @return:
- * The number of bytes effectively read by the stream.
- *
- * @note:
- * This function performs a seek *or* a read operation depending on the
- * argument values. If `count` is zero, the operation is a seek to
- * `offset` bytes. If `count` is >~0, the operation is a read of `count`
- * bytes from the current position in the stream, and the `offset` value
- * should be ignored.
- *
- * For seek operations, a non-zero return value indicates an error.
+ * If count >~0, return the number of bytes effectively read by the
+ * stream (after seeking to `offset`). If count ==~0, return the status
+ * of the seek operation (non-zero indicates an error).
*
*/
typedef unsigned long
diff --git a/thirdparty/freetype/include/freetype/internal/compiler-macros.h b/thirdparty/freetype/include/freetype/internal/compiler-macros.h
index 7883317fed..6f67650979 100644
--- a/thirdparty/freetype/include/freetype/internal/compiler-macros.h
+++ b/thirdparty/freetype/include/freetype/internal/compiler-macros.h
@@ -41,8 +41,11 @@ FT_BEGIN_HEADER
# if ( defined( __STDC_VERSION__ ) && __STDC_VERSION__ > 201710L ) || \
( defined( __cplusplus ) && __cplusplus > 201402L )
# define FALL_THROUGH [[__fallthrough__]]
-# elif ( defined( __GNUC__ ) && __GNUC__ >= 7 ) || \
- ( defined( __clang__ ) && __clang_major__ >= 10 )
+# elif ( defined( __GNUC__ ) && __GNUC__ >= 7 ) || \
+ ( defined( __clang__ ) && \
+ ( defined( __apple_build_version__ ) \
+ ? __apple_build_version__ >= 12000000 \
+ : __clang_major__ >= 10 ) )
# define FALL_THROUGH __attribute__(( __fallthrough__ ))
# else
# define FALL_THROUGH ( (void)0 )
diff --git a/thirdparty/freetype/include/freetype/internal/ftdrv.h b/thirdparty/freetype/include/freetype/internal/ftdrv.h
index f78912ca0c..9001c07ad0 100644
--- a/thirdparty/freetype/include/freetype/internal/ftdrv.h
+++ b/thirdparty/freetype/include/freetype/internal/ftdrv.h
@@ -157,6 +157,7 @@ FT_BEGIN_HEADER
* A handle to a function used to select a new fixed size. It is used
* only if @FT_FACE_FLAG_FIXED_SIZES is set. Can be set to 0 if the
* scaling done in the base layer suffices.
+ *
* @note:
* Most function pointers, with the exception of `load_glyph`, can be set
* to 0 to indicate a default behaviour.
diff --git a/thirdparty/freetype/include/freetype/internal/ftmmtypes.h b/thirdparty/freetype/include/freetype/internal/ftmmtypes.h
index b7c66c35de..c4b21d6144 100644
--- a/thirdparty/freetype/include/freetype/internal/ftmmtypes.h
+++ b/thirdparty/freetype/include/freetype/internal/ftmmtypes.h
@@ -28,13 +28,19 @@ FT_BEGIN_HEADER
typedef struct GX_ItemVarDataRec_
{
- FT_UInt itemCount; /* number of delta sets per item */
- FT_UInt regionIdxCount; /* number of region indices */
- FT_UInt* regionIndices; /* array of `regionCount' indices; */
- /* these index `varRegionList' */
- FT_ItemVarDelta* deltaSet; /* array of `itemCount' deltas */
- /* use `innerIndex' for this array */
-
+ FT_UInt itemCount; /* Number of delta sets per item. */
+ FT_UInt regionIdxCount; /* Number of region indices. */
+ FT_UInt* regionIndices; /* Array of `regionCount` indices; */
+ /* these index `varRegionList`. */
+ FT_Byte* deltaSet; /* Array of `itemCount` deltas; */
+ /* use `innerIndex` for this array. */
+ FT_UShort wordDeltaCount; /* Number of the first 32-bit ints */
+ /* or 16-bit ints of `deltaSet` */
+ /* depending on `longWords`. */
+ FT_Bool longWords; /* If true, `deltaSet` is a 32-bit */
+ /* array followed by a 16-bit */
+ /* array, otherwise a 16-bit array */
+ /* followed by an 8-bit array. */
} GX_ItemVarDataRec, *GX_ItemVarData;
diff --git a/thirdparty/freetype/include/freetype/internal/services/svmetric.h b/thirdparty/freetype/include/freetype/internal/services/svmetric.h
index e588ea4872..167617ebb3 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svmetric.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svmetric.h
@@ -77,6 +77,9 @@ FT_BEGIN_HEADER
typedef void
(*FT_Metrics_Adjust_Func)( FT_Face face );
+ typedef FT_Error
+ (*FT_Size_Reset_Func)( FT_Size size );
+
FT_DEFINE_SERVICE( MetricsVariations )
{
@@ -90,6 +93,7 @@ FT_BEGIN_HEADER
FT_VOrg_Adjust_Func vorg_adjust;
FT_Metrics_Adjust_Func metrics_adjust;
+ FT_Size_Reset_Func size_reset;
};
@@ -101,7 +105,8 @@ FT_BEGIN_HEADER
tsb_adjust_, \
bsb_adjust_, \
vorg_adjust_, \
- metrics_adjust_ ) \
+ metrics_adjust_, \
+ size_reset_ ) \
static const FT_Service_MetricsVariationsRec class_ = \
{ \
hadvance_adjust_, \
@@ -111,7 +116,8 @@ FT_BEGIN_HEADER
tsb_adjust_, \
bsb_adjust_, \
vorg_adjust_, \
- metrics_adjust_ \
+ metrics_adjust_, \
+ size_reset_ \
};
/* */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svmm.h b/thirdparty/freetype/include/freetype/internal/services/svmm.h
index d94204232e..7e76ab8324 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svmm.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svmm.h
@@ -60,9 +60,9 @@ FT_BEGIN_HEADER
/* use return value -1 to indicate that the new coordinates */
/* are equal to the current ones; no changes are thus needed */
typedef FT_Error
- (*FT_Set_MM_Blend_Func)( FT_Face face,
- FT_UInt num_coords,
- FT_Long* coords );
+ (*FT_Set_MM_Blend_Func)( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords );
typedef FT_Error
(*FT_Get_Var_Design_Func)( FT_Face face,
@@ -70,13 +70,17 @@ FT_BEGIN_HEADER
FT_Fixed* coords );
typedef FT_Error
- (*FT_Set_Instance_Func)( FT_Face face,
- FT_UInt instance_index );
+ (*FT_Set_Named_Instance_Func)( FT_Face face,
+ FT_UInt instance_index );
typedef FT_Error
- (*FT_Get_MM_Blend_Func)( FT_Face face,
- FT_UInt num_coords,
- FT_Long* coords );
+ (*FT_Get_Default_Named_Instance_Func)( FT_Face face,
+ FT_UInt *instance_index );
+
+ typedef FT_Error
+ (*FT_Get_MM_Blend_Func)( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords );
typedef FT_Error
(*FT_Get_Var_Blend_Func)( FT_Face face,
@@ -86,7 +90,7 @@ FT_BEGIN_HEADER
FT_MM_Var* *mm_var );
typedef void
- (*FT_Done_Blend_Func)( FT_Face );
+ (*FT_Done_Blend_Func)( FT_Face face );
typedef FT_Error
(*FT_Set_MM_WeightVector_Func)( FT_Face face,
@@ -98,6 +102,9 @@ FT_BEGIN_HEADER
FT_UInt* len,
FT_Fixed* weight_vector );
+ typedef void
+ (*FT_Construct_PS_Name_Func)( FT_Face face );
+
typedef FT_Error
(*FT_Var_Load_Delta_Set_Idx_Map_Func)( FT_Face face,
FT_ULong offset,
@@ -134,11 +141,13 @@ FT_BEGIN_HEADER
FT_Get_MM_Var_Func get_mm_var;
FT_Set_Var_Design_Func set_var_design;
FT_Get_Var_Design_Func get_var_design;
- FT_Set_Instance_Func set_instance;
+ FT_Set_Named_Instance_Func set_named_instance;
+ FT_Get_Default_Named_Instance_Func get_default_named_instance;
FT_Set_MM_WeightVector_Func set_mm_weightvector;
FT_Get_MM_WeightVector_Func get_mm_weightvector;
/* for internal use; only needed for code sharing between modules */
+ FT_Construct_PS_Name_Func construct_ps_name;
FT_Var_Load_Delta_Set_Idx_Map_Func load_delta_set_idx_map;
FT_Var_Load_Item_Var_Store_Func load_item_var_store;
FT_Var_Get_Item_Delta_Func get_item_delta;
@@ -149,43 +158,49 @@ FT_BEGIN_HEADER
};
-#define FT_DEFINE_SERVICE_MULTIMASTERSREC( class_, \
- get_mm_, \
- set_mm_design_, \
- set_mm_blend_, \
- get_mm_blend_, \
- get_mm_var_, \
- set_var_design_, \
- get_var_design_, \
- set_instance_, \
- set_weightvector_, \
- get_weightvector_, \
- load_delta_set_idx_map_, \
- load_item_var_store_, \
- get_item_delta_, \
- done_item_var_store_, \
- done_delta_set_idx_map_, \
- get_var_blend_, \
- done_blend_ ) \
- static const FT_Service_MultiMastersRec class_ = \
- { \
- get_mm_, \
- set_mm_design_, \
- set_mm_blend_, \
- get_mm_blend_, \
- get_mm_var_, \
- set_var_design_, \
- get_var_design_, \
- set_instance_, \
- set_weightvector_, \
- get_weightvector_, \
- load_delta_set_idx_map_, \
- load_item_var_store_, \
- get_item_delta_, \
- done_item_var_store_, \
- done_delta_set_idx_map_, \
- get_var_blend_, \
- done_blend_ \
+#define FT_DEFINE_SERVICE_MULTIMASTERSREC( class_, \
+ get_mm_, \
+ set_mm_design_, \
+ set_mm_blend_, \
+ get_mm_blend_, \
+ get_mm_var_, \
+ set_var_design_, \
+ get_var_design_, \
+ set_named_instance_, \
+ get_default_named_instance_, \
+ set_mm_weightvector_, \
+ get_mm_weightvector_, \
+ \
+ construct_ps_name_, \
+ load_delta_set_idx_map_, \
+ load_item_var_store_, \
+ get_item_delta_, \
+ done_item_var_store_, \
+ done_delta_set_idx_map_, \
+ get_var_blend_, \
+ done_blend_ ) \
+ static const FT_Service_MultiMastersRec class_ = \
+ { \
+ get_mm_, \
+ set_mm_design_, \
+ set_mm_blend_, \
+ get_mm_blend_, \
+ get_mm_var_, \
+ set_var_design_, \
+ get_var_design_, \
+ set_named_instance_, \
+ get_default_named_instance_, \
+ set_mm_weightvector_, \
+ get_mm_weightvector_, \
+ \
+ construct_ps_name_, \
+ load_delta_set_idx_map_, \
+ load_item_var_store_, \
+ get_item_delta_, \
+ done_item_var_store_, \
+ done_delta_set_idx_map_, \
+ get_var_blend_, \
+ done_blend_ \
};
/* */
diff --git a/thirdparty/freetype/include/freetype/internal/services/svpscmap.h b/thirdparty/freetype/include/freetype/internal/services/svpscmap.h
index fd99d857e4..6e599f3aab 100644
--- a/thirdparty/freetype/include/freetype/internal/services/svpscmap.h
+++ b/thirdparty/freetype/include/freetype/internal/services/svpscmap.h
@@ -97,7 +97,7 @@ FT_BEGIN_HEADER
(*PS_Unicodes_CharIndexFunc)( PS_Unicodes unicodes,
FT_UInt32 unicode );
- typedef FT_UInt32
+ typedef FT_UInt
(*PS_Unicodes_CharNextFunc)( PS_Unicodes unicodes,
FT_UInt32 *unicode );
diff --git a/thirdparty/freetype/include/freetype/internal/t1types.h b/thirdparty/freetype/include/freetype/internal/t1types.h
index 5a105c5879..b9c94398fd 100644
--- a/thirdparty/freetype/include/freetype/internal/t1types.h
+++ b/thirdparty/freetype/include/freetype/internal/t1types.h
@@ -201,30 +201,30 @@ FT_BEGIN_HEADER
typedef struct T1_FaceRec_
{
- FT_FaceRec root;
- T1_FontRec type1;
- const void* psnames;
- const void* psaux;
- const void* afm_data;
- FT_CharMapRec charmaprecs[2];
- FT_CharMap charmaps[2];
+ FT_FaceRec root;
+ T1_FontRec type1;
+ const void* psnames;
+ const void* psaux;
+ const void* afm_data;
+ FT_CharMapRec charmaprecs[2];
+ FT_CharMap charmaps[2];
/* support for Multiple Masters fonts */
- PS_Blend blend;
+ PS_Blend blend;
/* undocumented, optional: indices of subroutines that express */
/* the NormalizeDesignVector and the ConvertDesignVector procedure, */
/* respectively, as Type 2 charstrings; -1 if keywords not present */
- FT_Int ndv_idx;
- FT_Int cdv_idx;
+ FT_Int ndv_idx;
+ FT_Int cdv_idx;
/* undocumented, optional: has the same meaning as len_buildchar */
/* for Type 2 fonts; manipulated by othersubrs 19, 24, and 25 */
- FT_UInt len_buildchar;
- FT_Long* buildchar;
+ FT_UInt len_buildchar;
+ FT_Long* buildchar;
/* since version 2.1 - interface to PostScript hinter */
- const void* pshinter;
+ const void* pshinter;
} T1_FaceRec;
diff --git a/thirdparty/freetype/include/freetype/internal/tttypes.h b/thirdparty/freetype/include/freetype/internal/tttypes.h
index 3b521924ca..984121a0e4 100644
--- a/thirdparty/freetype/include/freetype/internal/tttypes.h
+++ b/thirdparty/freetype/include/freetype/internal/tttypes.h
@@ -779,13 +779,15 @@ FT_BEGIN_HEADER
/**************************************************************************
*
* @struct:
- * TT_Post_20Rec
+ * TT_Post_NamesRec
*
* @description:
- * Postscript names sub-table, format 2.0. Stores the PS name of each
- * glyph in the font face.
+ * Postscript names table, either format 2.0 or 2.5.
*
* @fields:
+ * loaded ::
+ * A flag to indicate whether the PS names are loaded.
+ *
* num_glyphs ::
* The number of named glyphs in the table.
*
@@ -798,68 +800,13 @@ FT_BEGIN_HEADER
* glyph_names ::
* The PS names not in Mac Encoding.
*/
- typedef struct TT_Post_20Rec_
+ typedef struct TT_Post_NamesRec_
{
+ FT_Bool loaded;
FT_UShort num_glyphs;
FT_UShort num_names;
FT_UShort* glyph_indices;
- FT_Char** glyph_names;
-
- } TT_Post_20Rec, *TT_Post_20;
-
-
- /**************************************************************************
- *
- * @struct:
- * TT_Post_25Rec
- *
- * @description:
- * Postscript names sub-table, format 2.5. Stores the PS name of each
- * glyph in the font face.
- *
- * @fields:
- * num_glyphs ::
- * The number of glyphs in the table.
- *
- * offsets ::
- * An array of signed offsets in a normal Mac Postscript name encoding.
- */
- typedef struct TT_Post_25_
- {
- FT_UShort num_glyphs;
- FT_Char* offsets;
-
- } TT_Post_25Rec, *TT_Post_25;
-
-
- /**************************************************************************
- *
- * @struct:
- * TT_Post_NamesRec
- *
- * @description:
- * Postscript names table, either format 2.0 or 2.5.
- *
- * @fields:
- * loaded ::
- * A flag to indicate whether the PS names are loaded.
- *
- * format_20 ::
- * The sub-table used for format 2.0.
- *
- * format_25 ::
- * The sub-table used for format 2.5.
- */
- typedef struct TT_Post_NamesRec_
- {
- FT_Bool loaded;
-
- union
- {
- TT_Post_20Rec format_20;
- TT_Post_25Rec format_25;
-
- } names;
+ FT_Byte** glyph_names;
} TT_Post_NamesRec, *TT_Post_Names;
@@ -1253,12 +1200,16 @@ FT_BEGIN_HEADER
* mm ::
* A pointer to the Multiple Masters service.
*
- * var ::
- * A pointer to the Metrics Variations service.
+ * tt_var ::
+ * A pointer to the Metrics Variations service for the "truetype"
+ * driver.
*
- * hdmx ::
- * The face's horizontal device metrics ('hdmx' table). This table is
- * optional in TrueType/OpenType fonts.
+ * face_var ::
+ * A pointer to the Metrics Variations service for this `TT_Face`'s
+ * driver.
+ *
+ * psaux ::
+ * A pointer to the PostScript Auxiliary service.
*
* gasp ::
* The grid-fitting and scaling properties table ('gasp'). This table
@@ -1364,6 +1315,12 @@ FT_BEGIN_HEADER
* var_postscript_prefix_len ::
* The length of the `var_postscript_prefix` string.
*
+ * var_default_named_instance ::
+ * The index of the default named instance.
+ *
+ * non_var_style_name ::
+ * The non-variation style name, used as a backup.
+ *
* horz_metrics_size ::
* The size of the 'hmtx' table.
*
@@ -1410,14 +1367,6 @@ FT_BEGIN_HEADER
* A mapping between the strike indices exposed by the API and the
* indices used in the font's sbit table.
*
- * cpal ::
- * A pointer to data related to the 'CPAL' table. `NULL` if the table
- * is not available.
- *
- * colr ::
- * A pointer to data related to the 'COLR' table. `NULL` if the table
- * is not available.
- *
* kern_table ::
* A pointer to the 'kern' table.
*
@@ -1458,6 +1407,18 @@ FT_BEGIN_HEADER
*
* ebdt_size ::
* The size of the sbit data table.
+ *
+ * cpal ::
+ * A pointer to data related to the 'CPAL' table. `NULL` if the table
+ * is not available.
+ *
+ * colr ::
+ * A pointer to data related to the 'COLR' table. `NULL` if the table
+ * is not available.
+ *
+ * svg ::
+ * A pointer to data related to the 'SVG' table. `NULL` if the table
+ * is not available.
*/
typedef struct TT_FaceRec_
{
@@ -1508,8 +1469,14 @@ FT_BEGIN_HEADER
void* mm;
/* a typeless pointer to the FT_Service_MetricsVariationsRec table */
- /* used to handle the HVAR, VVAR, and MVAR OpenType tables */
- void* var;
+ /* used to handle the HVAR, VVAR, and MVAR OpenType tables by the */
+ /* "truetype" driver */
+ void* tt_var;
+
+ /* a typeless pointer to the FT_Service_MetricsVariationsRec table */
+ /* used to handle the HVAR, VVAR, and MVAR OpenType tables by this */
+ /* TT_Face's driver */
+ void* face_var; /* since 2.13.1 */
#endif
/* a typeless pointer to the PostScript Aux service */
@@ -1591,6 +1558,9 @@ FT_BEGIN_HEADER
const char* var_postscript_prefix; /* since 2.7.2 */
FT_UInt var_postscript_prefix_len; /* since 2.7.2 */
+ FT_UInt var_default_named_instance; /* since 2.13.1 */
+
+ const char* non_var_style_name; /* since 2.13.1 */
#endif
/* since version 2.2 */
diff --git a/thirdparty/freetype/src/autofit/afcjk.c b/thirdparty/freetype/src/autofit/afcjk.c
index 5daefff359..af775b190c 100644
--- a/thirdparty/freetype/src/autofit/afcjk.c
+++ b/thirdparty/freetype/src/autofit/afcjk.c
@@ -417,16 +417,14 @@
{
FT_Int nn;
- FT_Int first = 0;
- FT_Int last = -1;
+ FT_Int pp, first, last;
- for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ )
+ last = -1;
+ for ( nn = 0; nn < outline.n_contours; nn++ )
{
- FT_Int pp;
-
-
- last = outline.contours[nn];
+ first = last + 1;
+ last = outline.contours[nn];
/* Avoid single-point contours since they are never rasterized. */
/* In some fonts, they correspond to mark attachment points */
@@ -569,8 +567,8 @@
af_cjk_metrics_check_digits( AF_CJKMetrics metrics,
FT_Face face )
{
- FT_Bool started = 0, same_width = 1;
- FT_Fixed advance = 0, old_advance = 0;
+ FT_Bool started = 0, same_width = 1;
+ FT_Long advance = 0, old_advance = 0;
/* If HarfBuzz is not available, we need a pointer to a single */
/* unsigned long value. */
@@ -635,10 +633,11 @@
/* Initialize global metrics. */
FT_LOCAL_DEF( FT_Error )
- af_cjk_metrics_init( AF_CJKMetrics metrics,
- FT_Face face )
+ af_cjk_metrics_init( AF_StyleMetrics metrics_, /* AF_CJKMetrics */
+ FT_Face face )
{
- FT_CharMap oldmap = face->charmap;
+ AF_CJKMetrics metrics = (AF_CJKMetrics)metrics_;
+ FT_CharMap oldmap = face->charmap;
metrics->units_per_em = face->units_per_EM;
@@ -756,9 +755,12 @@
/* Scale global values in both directions. */
FT_LOCAL_DEF( void )
- af_cjk_metrics_scale( AF_CJKMetrics metrics,
- AF_Scaler scaler )
+ af_cjk_metrics_scale( AF_StyleMetrics metrics_, /* AF_CJKMetrics */
+ AF_Scaler scaler )
{
+ AF_CJKMetrics metrics = (AF_CJKMetrics)metrics_;
+
+
/* we copy the whole structure since the x and y scaling values */
/* are not modified, contrary to e.g. the `latin' auto-hinter */
metrics->root.scaler = *scaler;
@@ -771,11 +773,14 @@
/* Extract standard_width from writing system/script specific */
/* metrics class. */
- FT_LOCAL_DEF( void )
- af_cjk_get_standard_widths( AF_CJKMetrics metrics,
- FT_Pos* stdHW,
- FT_Pos* stdVW )
+ FT_CALLBACK_DEF( void )
+ af_cjk_get_standard_widths( AF_StyleMetrics metrics_, /* AF_CJKMetrics */
+ FT_Pos* stdHW,
+ FT_Pos* stdVW )
{
+ AF_CJKMetrics metrics = (AF_CJKMetrics)metrics_;
+
+
if ( stdHW )
*stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
@@ -1376,9 +1381,10 @@
/* Initalize hinting engine. */
FT_LOCAL_DEF( FT_Error )
- af_cjk_hints_init( AF_GlyphHints hints,
- AF_CJKMetrics metrics )
+ af_cjk_hints_init( AF_GlyphHints hints,
+ AF_StyleMetrics metrics_ ) /* AF_CJKMetrics */
{
+ AF_CJKMetrics metrics = (AF_CJKMetrics)metrics_;
FT_Render_Mode mode;
FT_UInt32 scaler_flags, other_flags;
@@ -2268,11 +2274,13 @@
/* Apply the complete hinting algorithm to a CJK glyph. */
FT_LOCAL_DEF( FT_Error )
- af_cjk_hints_apply( FT_UInt glyph_index,
- AF_GlyphHints hints,
- FT_Outline* outline,
- AF_CJKMetrics metrics )
+ af_cjk_hints_apply( FT_UInt glyph_index,
+ AF_GlyphHints hints,
+ FT_Outline* outline,
+ AF_StyleMetrics metrics_ ) /* AF_CJKMetrics */
{
+ AF_CJKMetrics metrics = (AF_CJKMetrics)metrics_;
+
FT_Error error;
int dim;
diff --git a/thirdparty/freetype/src/autofit/afcjk.h b/thirdparty/freetype/src/autofit/afcjk.h
index bd7b81b3e2..f380ef6e03 100644
--- a/thirdparty/freetype/src/autofit/afcjk.h
+++ b/thirdparty/freetype/src/autofit/afcjk.h
@@ -103,22 +103,22 @@ FT_BEGIN_HEADER
#ifdef AF_CONFIG_OPTION_CJK
FT_LOCAL( FT_Error )
- af_cjk_metrics_init( AF_CJKMetrics metrics,
- FT_Face face );
+ af_cjk_metrics_init( AF_StyleMetrics metrics,
+ FT_Face face );
FT_LOCAL( void )
- af_cjk_metrics_scale( AF_CJKMetrics metrics,
- AF_Scaler scaler );
+ af_cjk_metrics_scale( AF_StyleMetrics metrics,
+ AF_Scaler scaler );
FT_LOCAL( FT_Error )
- af_cjk_hints_init( AF_GlyphHints hints,
- AF_CJKMetrics metrics );
+ af_cjk_hints_init( AF_GlyphHints hints,
+ AF_StyleMetrics metrics );
FT_LOCAL( FT_Error )
- af_cjk_hints_apply( FT_UInt glyph_index,
- AF_GlyphHints hints,
- FT_Outline* outline,
- AF_CJKMetrics metrics );
+ af_cjk_hints_apply( FT_UInt glyph_index,
+ AF_GlyphHints hints,
+ FT_Outline* outline,
+ AF_StyleMetrics metrics );
/* shared; called from afindic.c */
FT_LOCAL( void )
diff --git a/thirdparty/freetype/src/autofit/afglobal.c b/thirdparty/freetype/src/autofit/afglobal.c
index ede27eb166..b1957570f0 100644
--- a/thirdparty/freetype/src/autofit/afglobal.c
+++ b/thirdparty/freetype/src/autofit/afglobal.c
@@ -376,8 +376,11 @@
FT_LOCAL_DEF( void )
- af_face_globals_free( AF_FaceGlobals globals )
+ af_face_globals_free( void* globals_ )
{
+ AF_FaceGlobals globals = (AF_FaceGlobals)globals_;
+
+
if ( globals )
{
FT_Memory memory = globals->face->memory;
diff --git a/thirdparty/freetype/src/autofit/afglobal.h b/thirdparty/freetype/src/autofit/afglobal.h
index 83a7c2ff15..66170e419d 100644
--- a/thirdparty/freetype/src/autofit/afglobal.h
+++ b/thirdparty/freetype/src/autofit/afglobal.h
@@ -156,7 +156,7 @@ FT_BEGIN_HEADER
AF_StyleMetrics *ametrics );
FT_LOCAL( void )
- af_face_globals_free( AF_FaceGlobals globals );
+ af_face_globals_free( void* globals );
FT_LOCAL( FT_Bool )
af_face_globals_is_digit( AF_FaceGlobals globals,
diff --git a/thirdparty/freetype/src/autofit/afhints.c b/thirdparty/freetype/src/autofit/afhints.c
index 6515af9f04..e4a378fbf7 100644
--- a/thirdparty/freetype/src/autofit/afhints.c
+++ b/thirdparty/freetype/src/autofit/afhints.c
@@ -320,8 +320,9 @@
static char*
- af_print_idx( char* p,
- int idx )
+ af_print_idx( char* p,
+ size_t n,
+ int idx )
{
if ( idx == -1 )
{
@@ -330,7 +331,7 @@
p[2] = '\0';
}
else
- ft_sprintf( p, "%d", idx );
+ ft_snprintf( p, n, "%d", idx );
return p;
}
@@ -457,12 +458,12 @@
" %5d %5d %7.2f %7.2f %7.2f %7.2f"
" %5s %5s %5s %5s\n",
point_idx,
- af_print_idx( buf1,
+ af_print_idx( buf1, 16,
af_get_edge_index( hints, segment_idx_1, 1 ) ),
- af_print_idx( buf2, segment_idx_1 ),
- af_print_idx( buf3,
+ af_print_idx( buf2, 16, segment_idx_1 ),
+ af_print_idx( buf3, 16,
af_get_edge_index( hints, segment_idx_0, 0 ) ),
- af_print_idx( buf4, segment_idx_0 ),
+ af_print_idx( buf4, 16, segment_idx_0 ),
( point->flags & AF_FLAG_NEAR )
? " near "
: ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
@@ -476,18 +477,22 @@
(double)point->x / 64,
(double)point->y / 64,
- af_print_idx( buf5, af_get_strong_edge_index( hints,
- point->before,
- 1 ) ),
- af_print_idx( buf6, af_get_strong_edge_index( hints,
- point->after,
- 1 ) ),
- af_print_idx( buf7, af_get_strong_edge_index( hints,
- point->before,
- 0 ) ),
- af_print_idx( buf8, af_get_strong_edge_index( hints,
- point->after,
- 0 ) ) ));
+ af_print_idx( buf5, 16,
+ af_get_strong_edge_index( hints,
+ point->before,
+ 1 ) ),
+ af_print_idx( buf6, 16,
+ af_get_strong_edge_index( hints,
+ point->after,
+ 1 ) ),
+ af_print_idx( buf7, 16,
+ af_get_strong_edge_index( hints,
+ point->before,
+ 0 ) ),
+ af_print_idx( buf8, 16,
+ af_get_strong_edge_index( hints,
+ point->after,
+ 0 ) ) ));
}
AF_DUMP(( "\n" ));
}
@@ -574,9 +579,12 @@
AF_INDEX_NUM( seg->first, points ),
AF_INDEX_NUM( seg->last, points ),
- af_print_idx( buf1, AF_INDEX_NUM( seg->link, segments ) ),
- af_print_idx( buf2, AF_INDEX_NUM( seg->serif, segments ) ),
- af_print_idx( buf3, AF_INDEX_NUM( seg->edge, edges ) ),
+ af_print_idx( buf1, 16,
+ AF_INDEX_NUM( seg->link, segments ) ),
+ af_print_idx( buf2, 16,
+ AF_INDEX_NUM( seg->serif, segments ) ),
+ af_print_idx( buf3, 16,
+ AF_INDEX_NUM( seg->edge, edges ) ),
seg->height,
seg->height - ( seg->max_coord - seg->min_coord ),
@@ -716,8 +724,10 @@
AF_INDEX_NUM( edge, edges ),
(double)(int)edge->opos / 64,
af_dir_str( (AF_Direction)edge->dir ),
- af_print_idx( buf1, AF_INDEX_NUM( edge->link, edges ) ),
- af_print_idx( buf2, AF_INDEX_NUM( edge->serif, edges ) ),
+ af_print_idx( buf1, 16,
+ AF_INDEX_NUM( edge->link, edges ) ),
+ af_print_idx( buf2, 16,
+ AF_INDEX_NUM( edge->serif, edges ) ),
edge->blue_edge ? 'y' : 'n',
(double)edge->opos / 64,
diff --git a/thirdparty/freetype/src/autofit/afindic.c b/thirdparty/freetype/src/autofit/afindic.c
index 289a09d71d..7fb12c63d5 100644
--- a/thirdparty/freetype/src/autofit/afindic.c
+++ b/thirdparty/freetype/src/autofit/afindic.c
@@ -28,9 +28,12 @@
static FT_Error
- af_indic_metrics_init( AF_CJKMetrics metrics,
- FT_Face face )
+ af_indic_metrics_init( AF_StyleMetrics metrics_, /* AF_CJKMetrics */
+ FT_Face face )
{
+ AF_CJKMetrics metrics = (AF_CJKMetrics)metrics_;
+
+
/* skip blue zone init in CJK routines */
FT_CharMap oldmap = face->charmap;
@@ -55,8 +58,8 @@
static void
- af_indic_metrics_scale( AF_CJKMetrics metrics,
- AF_Scaler scaler )
+ af_indic_metrics_scale( AF_StyleMetrics metrics,
+ AF_Scaler scaler )
{
/* use CJK routines */
af_cjk_metrics_scale( metrics, scaler );
@@ -64,8 +67,8 @@
static FT_Error
- af_indic_hints_init( AF_GlyphHints hints,
- AF_CJKMetrics metrics )
+ af_indic_hints_init( AF_GlyphHints hints,
+ AF_StyleMetrics metrics )
{
/* use CJK routines */
return af_cjk_hints_init( hints, metrics );
@@ -73,10 +76,10 @@
static FT_Error
- af_indic_hints_apply( FT_UInt glyph_index,
- AF_GlyphHints hints,
- FT_Outline* outline,
- AF_CJKMetrics metrics )
+ af_indic_hints_apply( FT_UInt glyph_index,
+ AF_GlyphHints hints,
+ FT_Outline* outline,
+ AF_StyleMetrics metrics )
{
/* use CJK routines */
return af_cjk_hints_apply( glyph_index, hints, outline, metrics );
@@ -87,10 +90,13 @@
/* metrics class. */
static void
- af_indic_get_standard_widths( AF_CJKMetrics metrics,
- FT_Pos* stdHW,
- FT_Pos* stdVW )
+ af_indic_get_standard_widths( AF_StyleMetrics metrics_, /* AF_CJKMetrics */
+ FT_Pos* stdHW,
+ FT_Pos* stdVW )
{
+ AF_CJKMetrics metrics = (AF_CJKMetrics)metrics_;
+
+
if ( stdHW )
*stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
diff --git a/thirdparty/freetype/src/autofit/aflatin.c b/thirdparty/freetype/src/autofit/aflatin.c
index 4b3c59b3c3..46c6e450a8 100644
--- a/thirdparty/freetype/src/autofit/aflatin.c
+++ b/thirdparty/freetype/src/autofit/aflatin.c
@@ -496,23 +496,20 @@
/* now compute min or max point indices and coordinates */
points = outline.points;
best_point = -1;
+ best_contour_first = -1;
+ best_contour_last = -1;
best_y = 0; /* make compiler happy */
- best_contour_first = 0; /* ditto */
- best_contour_last = 0; /* ditto */
{
FT_Int nn;
- FT_Int first = 0;
- FT_Int last = -1;
+ FT_Int pp, first, last;
- for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ )
+ last = -1;
+ for ( nn = 0; nn < outline.n_contours; nn++ )
{
- FT_Int old_best_point = best_point;
- FT_Int pp;
-
-
- last = outline.contours[nn];
+ first = last + 1;
+ last = outline.contours[nn];
/* Avoid single-point contours since they are never */
/* rasterized. In some fonts, they correspond to mark */
@@ -551,7 +548,7 @@
}
}
- if ( best_point != old_best_point )
+ if ( best_point > best_contour_last )
{
best_contour_first = first;
best_contour_last = last;
@@ -1068,8 +1065,8 @@
af_latin_metrics_check_digits( AF_LatinMetrics metrics,
FT_Face face )
{
- FT_Bool started = 0, same_width = 1;
- FT_Fixed advance = 0, old_advance = 0;
+ FT_Bool started = 0, same_width = 1;
+ FT_Long advance = 0, old_advance = 0;
/* If HarfBuzz is not available, we need a pointer to a single */
/* unsigned long value. */
@@ -1134,9 +1131,11 @@
/* Initialize global metrics. */
FT_LOCAL_DEF( FT_Error )
- af_latin_metrics_init( AF_LatinMetrics metrics,
+ af_latin_metrics_init( AF_StyleMetrics metrics_, /* AF_LatinMetrics */
FT_Face face )
{
+ AF_LatinMetrics metrics = (AF_LatinMetrics)metrics_;
+
FT_Error error = FT_Err_Ok;
FT_CharMap oldmap = face->charmap;
@@ -1489,9 +1488,12 @@
/* Scale global values in both directions. */
FT_LOCAL_DEF( void )
- af_latin_metrics_scale( AF_LatinMetrics metrics,
+ af_latin_metrics_scale( AF_StyleMetrics metrics_, /* AF_LatinMetrics */
AF_Scaler scaler )
{
+ AF_LatinMetrics metrics = (AF_LatinMetrics)metrics_;
+
+
metrics->root.scaler.render_mode = scaler->render_mode;
metrics->root.scaler.face = scaler->face;
metrics->root.scaler.flags = scaler->flags;
@@ -1504,11 +1506,14 @@
/* Extract standard_width from writing system/script specific */
/* metrics class. */
- FT_LOCAL_DEF( void )
- af_latin_get_standard_widths( AF_LatinMetrics metrics,
+ FT_CALLBACK_DEF( void )
+ af_latin_get_standard_widths( AF_StyleMetrics metrics_, /* AF_LatinMetrics */
FT_Pos* stdHW,
FT_Pos* stdVW )
{
+ AF_LatinMetrics metrics = (AF_LatinMetrics)metrics_;
+
+
if ( stdHW )
*stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
@@ -2041,7 +2046,7 @@
max = seg2->max_coord;
/* compute maximum coordinate difference of the two segments */
- /* (this is, how much they overlap) */
+ /* (that is, how much they overlap) */
len = max - min;
if ( len >= len_threshold )
{
@@ -2610,8 +2615,10 @@
static FT_Error
af_latin_hints_init( AF_GlyphHints hints,
- AF_LatinMetrics metrics )
+ AF_StyleMetrics metrics_ ) /* AF_LatinMetrics */
{
+ AF_LatinMetrics metrics = (AF_LatinMetrics)metrics_;
+
FT_Render_Mode mode;
FT_UInt32 scaler_flags, other_flags;
FT_Face face = metrics->root.scaler.face;
@@ -3547,8 +3554,10 @@
af_latin_hints_apply( FT_UInt glyph_index,
AF_GlyphHints hints,
FT_Outline* outline,
- AF_LatinMetrics metrics )
+ AF_StyleMetrics metrics_ ) /* AF_LatinMetrics */
{
+ AF_LatinMetrics metrics = (AF_LatinMetrics)metrics_;
+
FT_Error error;
int dim;
diff --git a/thirdparty/freetype/src/autofit/aflatin.h b/thirdparty/freetype/src/autofit/aflatin.h
index 3c6a7ee4f6..31aa91d3bd 100644
--- a/thirdparty/freetype/src/autofit/aflatin.h
+++ b/thirdparty/freetype/src/autofit/aflatin.h
@@ -116,11 +116,11 @@ FT_BEGIN_HEADER
FT_LOCAL( FT_Error )
- af_latin_metrics_init( AF_LatinMetrics metrics,
+ af_latin_metrics_init( AF_StyleMetrics metrics,
FT_Face face );
FT_LOCAL( void )
- af_latin_metrics_scale( AF_LatinMetrics metrics,
+ af_latin_metrics_scale( AF_StyleMetrics metrics,
AF_Scaler scaler );
FT_LOCAL( void )
diff --git a/thirdparty/freetype/src/autofit/afloader.c b/thirdparty/freetype/src/autofit/afloader.c
index c8082796fe..7c47d562af 100644
--- a/thirdparty/freetype/src/autofit/afloader.c
+++ b/thirdparty/freetype/src/autofit/afloader.c
@@ -55,10 +55,8 @@
error = af_face_globals_new( face, &loader->globals, module );
if ( !error )
{
- face->autohint.data =
- (FT_Pointer)loader->globals;
- face->autohint.finalizer =
- (FT_Generic_Finalizer)af_face_globals_free;
+ face->autohint.data = (FT_Pointer)loader->globals;
+ face->autohint.finalizer = af_face_globals_free;
}
}
diff --git a/thirdparty/freetype/src/autofit/afmodule.c b/thirdparty/freetype/src/autofit/afmodule.c
index 92e5156ab2..20a6b96bc4 100644
--- a/thirdparty/freetype/src/autofit/afmodule.c
+++ b/thirdparty/freetype/src/autofit/afmodule.c
@@ -89,10 +89,8 @@
error = af_face_globals_new( face, &globals, module );
if ( !error )
{
- face->autohint.data =
- (FT_Pointer)globals;
- face->autohint.finalizer =
- (FT_Generic_Finalizer)af_face_globals_free;
+ face->autohint.data = (FT_Pointer)globals;
+ face->autohint.finalizer = af_face_globals_free;
}
}
@@ -374,8 +372,9 @@
FT_DEFINE_SERVICE_PROPERTIESREC(
af_service_properties,
- (FT_Properties_SetFunc)af_property_set, /* set_property */
- (FT_Properties_GetFunc)af_property_get ) /* get_property */
+ af_property_set, /* FT_Properties_SetFunc set_property */
+ af_property_get /* FT_Properties_GetFunc get_property */
+ )
FT_DEFINE_SERVICEDESCREC1(
@@ -430,12 +429,14 @@
FT_CALLBACK_DEF( FT_Error )
- af_autofitter_load_glyph( AF_Module module,
- FT_GlyphSlot slot,
- FT_Size size,
- FT_UInt glyph_index,
- FT_Int32 load_flags )
+ af_autofitter_load_glyph( FT_AutoHinter module_,
+ FT_GlyphSlot slot,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
{
+ AF_Module module = (AF_Module)module_;
+
FT_Error error = FT_Err_Ok;
FT_Memory memory = module->root.library->memory;
@@ -499,10 +500,10 @@
FT_DEFINE_AUTOHINTER_INTERFACE(
af_autofitter_interface,
- NULL, /* reset_face */
- NULL, /* get_global_hints */
- NULL, /* done_global_hints */
- (FT_AutoHinter_GlyphLoadFunc)af_autofitter_load_glyph /* load_glyph */
+ NULL, /* FT_AutoHinter_GlobalResetFunc reset_face */
+ NULL, /* FT_AutoHinter_GlobalGetFunc get_global_hints */
+ NULL, /* FT_AutoHinter_GlobalDoneFunc done_global_hints */
+ af_autofitter_load_glyph /* FT_AutoHinter_GlyphLoadFunc load_glyph */
)
FT_DEFINE_MODULE(
@@ -517,9 +518,9 @@
(const void*)&af_autofitter_interface,
- (FT_Module_Constructor)af_autofitter_init, /* module_init */
- (FT_Module_Destructor) af_autofitter_done, /* module_done */
- (FT_Module_Requester) af_get_interface /* get_interface */
+ af_autofitter_init, /* FT_Module_Constructor module_init */
+ af_autofitter_done, /* FT_Module_Destructor module_done */
+ af_get_interface /* FT_Module_Requester get_interface */
)
diff --git a/thirdparty/freetype/src/autofit/afshaper.c b/thirdparty/freetype/src/autofit/afshaper.c
index 1b8b870e89..abc6f1d292 100644
--- a/thirdparty/freetype/src/autofit/afshaper.c
+++ b/thirdparty/freetype/src/autofit/afshaper.c
@@ -258,7 +258,7 @@
/*
* We now check whether we can construct blue zones, using glyphs
* covered by the feature only. In case there is not a single zone
- * (this is, not a single character is covered), we skip this coverage.
+ * (that is, not a single character is covered), we skip this coverage.
*
*/
if ( style_class->coverage != AF_COVERAGE_DEFAULT )
@@ -313,9 +313,9 @@
* hinted and usually rendered glyph.
*
* Consider the superscript feature of font `pala.ttf': Some of the
- * glyphs are `real', this is, they have a zero vertical offset, but
+ * glyphs are `real', that is, they have a zero vertical offset, but
* most of them are small caps glyphs shifted up to the superscript
- * position (this is, the `sups' feature is present in both the GSUB and
+ * position (that is, the `sups' feature is present in both the GSUB and
* GPOS tables). The code for blue zones computation actually uses a
* feature's y offset so that the `real' glyphs get correct hints. But
* later on it is impossible to decide whether a glyph index belongs to,
diff --git a/thirdparty/freetype/src/autofit/ft-hb.c b/thirdparty/freetype/src/autofit/ft-hb.c
index 09a8401c4a..71aee04550 100644
--- a/thirdparty/freetype/src/autofit/ft-hb.c
+++ b/thirdparty/freetype/src/autofit/ft-hb.c
@@ -108,7 +108,7 @@ hb_ft_font_create_ (FT_Face ft_face,
#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
/* ANSI C doesn't like empty source files */
-typedef int _ft_hb_dummy;
+typedef int ft_hb_dummy_;
#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
diff --git a/thirdparty/freetype/src/base/ftbbox.c b/thirdparty/freetype/src/base/ftbbox.c
index 7dd71882ea..385fea4040 100644
--- a/thirdparty/freetype/src/base/ftbbox.c
+++ b/thirdparty/freetype/src/base/ftbbox.c
@@ -82,10 +82,13 @@
* @Return:
* Always 0. Needed for the interface only.
*/
- static int
- BBox_Move_To( FT_Vector* to,
- TBBox_Rec* user )
+ FT_CALLBACK_DEF( int )
+ BBox_Move_To( const FT_Vector* to,
+ void* user_ )
{
+ TBBox_Rec* user = (TBBox_Rec*)user_;
+
+
FT_UPDATE_BBOX( to, user->bbox );
user->last = *to;
@@ -116,10 +119,13 @@
* @Return:
* Always 0. Needed for the interface only.
*/
- static int
- BBox_Line_To( FT_Vector* to,
- TBBox_Rec* user )
+ FT_CALLBACK_DEF( int )
+ BBox_Line_To( const FT_Vector* to,
+ void* user_ )
{
+ TBBox_Rec* user = (TBBox_Rec*)user_;
+
+
user->last = *to;
return 0;
@@ -205,11 +211,14 @@
* In the case of a non-monotonous arc, we compute directly the
* extremum coordinates, as it is sufficiently fast.
*/
- static int
- BBox_Conic_To( FT_Vector* control,
- FT_Vector* to,
- TBBox_Rec* user )
+ FT_CALLBACK_DEF( int )
+ BBox_Conic_To( const FT_Vector* control,
+ const FT_Vector* to,
+ void* user_ )
{
+ TBBox_Rec* user = (TBBox_Rec*)user_;
+
+
/* in case `to' is implicit and not included in bbox yet */
FT_UPDATE_BBOX( to, user->bbox );
@@ -410,12 +419,15 @@
* In the case of a non-monotonous arc, we don't compute directly
* extremum coordinates, we subdivide instead.
*/
- static int
- BBox_Cubic_To( FT_Vector* control1,
- FT_Vector* control2,
- FT_Vector* to,
- TBBox_Rec* user )
+ FT_CALLBACK_DEF( int )
+ BBox_Cubic_To( const FT_Vector* control1,
+ const FT_Vector* control2,
+ const FT_Vector* to,
+ void* user_ )
{
+ TBBox_Rec* user = (TBBox_Rec*)user_;
+
+
/* We don't need to check `to' since it is always an on-point, */
/* thus within the bbox. Only segments with an off-point outside */
/* the bbox can possibly reach new extreme values. */
diff --git a/thirdparty/freetype/src/base/ftcalc.c b/thirdparty/freetype/src/base/ftcalc.c
index 13e74f3353..442b08ddf9 100644
--- a/thirdparty/freetype/src/base/ftcalc.c
+++ b/thirdparty/freetype/src/base/ftcalc.c
@@ -1061,7 +1061,7 @@
/* */
/* This approach has the advantage that the angle between */
/* `in' and `out' is not checked. In case one of the two */
- /* vectors is `dominant', this is, much larger than the */
+ /* vectors is `dominant', that is, much larger than the */
/* other vector, we thus always have a flat corner. */
/* */
/* hypotenuse */
@@ -1103,7 +1103,7 @@
for ( i = 0; i < count; ++i )
temp += (FT_Int64)s[i] * f[i];
- return ( temp + 0x8000 ) >> 16;
+ return (FT_Int32)( ( temp + 0x8000 ) >> 16 );
#else
temp.hi = 0;
temp.lo = 0;
diff --git a/thirdparty/freetype/src/base/ftdbgmem.c b/thirdparty/freetype/src/base/ftdbgmem.c
index 6730c4c8d3..8fab50dd01 100644
--- a/thirdparty/freetype/src/base/ftdbgmem.c
+++ b/thirdparty/freetype/src/base/ftdbgmem.c
@@ -963,7 +963,7 @@
#else /* !FT_DEBUG_MEMORY */
/* ANSI C doesn't like empty source files */
- typedef int _debug_mem_dummy;
+ typedef int debug_mem_dummy_;
#endif /* !FT_DEBUG_MEMORY */
diff --git a/thirdparty/freetype/src/base/ftmac.c b/thirdparty/freetype/src/base/ftmac.c
index de34e834f2..492d055384 100644
--- a/thirdparty/freetype/src/base/ftmac.c
+++ b/thirdparty/freetype/src/base/ftmac.c
@@ -1082,7 +1082,7 @@
#else /* !FT_MACINTOSH */
/* ANSI C doesn't like empty source files */
- typedef int _ft_mac_dummy;
+ typedef int ft_mac_dummy_;
#endif /* !FT_MACINTOSH */
diff --git a/thirdparty/freetype/src/base/ftmm.c b/thirdparty/freetype/src/base/ftmm.c
index a2b4bd03d7..9e2dd7ee79 100644
--- a/thirdparty/freetype/src/base/ftmm.c
+++ b/thirdparty/freetype/src/base/ftmm.c
@@ -185,6 +185,14 @@
error = FT_ERR( Invalid_Argument );
if ( service->set_mm_design )
error = service->set_mm_design( face, num_coords, coords );
+
+ if ( !error )
+ {
+ if ( num_coords )
+ face->face_flags |= FT_FACE_FLAG_VARIATION;
+ else
+ face->face_flags &= ~FT_FACE_FLAG_VARIATION;
+ }
}
/* enforce recomputation of auto-hinting data */
@@ -220,6 +228,14 @@
error = FT_ERR( Invalid_Argument );
if ( service->set_mm_weightvector )
error = service->set_mm_weightvector( face, len, weightvector );
+
+ if ( !error )
+ {
+ if ( len )
+ face->face_flags |= FT_FACE_FLAG_VARIATION;
+ else
+ face->face_flags &= ~FT_FACE_FLAG_VARIATION;
+ }
}
/* enforce recomputation of auto-hinting data */
@@ -283,6 +299,30 @@
if ( service_mm->set_var_design )
error = service_mm->set_var_design( face, num_coords, coords );
+ if ( !error || error == -1 )
+ {
+ FT_Bool is_variation_old = FT_IS_VARIATION( face );
+
+
+ if ( num_coords )
+ face->face_flags |= FT_FACE_FLAG_VARIATION;
+ else
+ face->face_flags &= ~FT_FACE_FLAG_VARIATION;
+
+ if ( service_mm->construct_ps_name )
+ {
+ if ( error == -1 )
+ {
+ /* The PS name of a named instance and a non-named instance */
+ /* usually differs, even if the axis values are identical. */
+ if ( is_variation_old != FT_IS_VARIATION( face ) )
+ service_mm->construct_ps_name( face );
+ }
+ else
+ service_mm->construct_ps_name( face );
+ }
+ }
+
/* internal error code -1 means `no change'; we can exit immediately */
if ( error == -1 )
return FT_Err_Ok;
@@ -359,6 +399,30 @@
if ( service_mm->set_mm_blend )
error = service_mm->set_mm_blend( face, num_coords, coords );
+ if ( !error || error == -1 )
+ {
+ FT_Bool is_variation_old = FT_IS_VARIATION( face );
+
+
+ if ( num_coords )
+ face->face_flags |= FT_FACE_FLAG_VARIATION;
+ else
+ face->face_flags &= ~FT_FACE_FLAG_VARIATION;
+
+ if ( service_mm->construct_ps_name )
+ {
+ if ( error == -1 )
+ {
+ /* The PS name of a named instance and a non-named instance */
+ /* usually differs, even if the axis values are identical. */
+ if ( is_variation_old != FT_IS_VARIATION( face ) )
+ service_mm->construct_ps_name( face );
+ }
+ else
+ service_mm->construct_ps_name( face );
+ }
+ }
+
/* internal error code -1 means `no change'; we can exit immediately */
if ( error == -1 )
return FT_Err_Ok;
@@ -410,6 +474,30 @@
if ( service_mm->set_mm_blend )
error = service_mm->set_mm_blend( face, num_coords, coords );
+ if ( !error || error == -1 )
+ {
+ FT_Bool is_variation_old = FT_IS_VARIATION( face );
+
+
+ if ( num_coords )
+ face->face_flags |= FT_FACE_FLAG_VARIATION;
+ else
+ face->face_flags &= ~FT_FACE_FLAG_VARIATION;
+
+ if ( service_mm->construct_ps_name )
+ {
+ if ( error == -1 )
+ {
+ /* The PS name of a named instance and a non-named instance */
+ /* usually differs, even if the axis values are identical. */
+ if ( is_variation_old != FT_IS_VARIATION( face ) )
+ service_mm->construct_ps_name( face );
+ }
+ else
+ service_mm->construct_ps_name( face );
+ }
+ }
+
/* internal error code -1 means `no change'; we can exit immediately */
if ( error == -1 )
return FT_Err_Ok;
@@ -535,8 +623,35 @@
if ( !error )
{
error = FT_ERR( Invalid_Argument );
- if ( service_mm->set_instance )
- error = service_mm->set_instance( face, instance_index );
+ if ( service_mm->set_named_instance )
+ error = service_mm->set_named_instance( face, instance_index );
+
+ if ( !error || error == -1 )
+ {
+ FT_Bool is_variation_old = FT_IS_VARIATION( face );
+
+
+ face->face_flags &= ~FT_FACE_FLAG_VARIATION;
+ face->face_index = ( instance_index << 16 ) |
+ ( face->face_index & 0xFFFFL );
+
+ if ( service_mm->construct_ps_name )
+ {
+ if ( error == -1 )
+ {
+ /* The PS name of a named instance and a non-named instance */
+ /* usually differs, even if the axis values are identical. */
+ if ( is_variation_old != FT_IS_VARIATION( face ) )
+ service_mm->construct_ps_name( face );
+ }
+ else
+ service_mm->construct_ps_name( face );
+ }
+ }
+
+ /* internal error code -1 means `no change'; we can exit immediately */
+ if ( error == -1 )
+ return FT_Err_Ok;
}
if ( !error )
@@ -554,11 +669,32 @@
face->autohint.data = NULL;
}
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Default_Named_Instance( FT_Face face,
+ FT_UInt *instance_index )
+ {
+ FT_Error error;
+
+ FT_Service_MultiMasters service_mm = NULL;
+
+
+ /* check of `face' delayed to `ft_face_get_mm_service' */
+
+ error = ft_face_get_mm_service( face, &service_mm );
if ( !error )
{
- face->face_index = ( instance_index << 16 ) |
- ( face->face_index & 0xFFFFL );
- face->face_flags &= ~FT_FACE_FLAG_VARIATION;
+ /* no error if `get_default_named_instance` is not available */
+ if ( service_mm->get_default_named_instance )
+ error = service_mm->get_default_named_instance( face,
+ instance_index );
+ else
+ error = FT_Err_Ok;
}
return error;
diff --git a/thirdparty/freetype/src/base/ftobjs.c b/thirdparty/freetype/src/base/ftobjs.c
index ad6ef0ae16..abfa3ab0e6 100644
--- a/thirdparty/freetype/src/base/ftobjs.c
+++ b/thirdparty/freetype/src/base/ftobjs.c
@@ -1019,7 +1019,8 @@
/* elegant. */
/* try to load SVG documents if available */
- if ( FT_HAS_SVG( face ) )
+ if ( ( load_flags & FT_LOAD_NO_SVG ) == 0 &&
+ FT_HAS_SVG( face ) )
{
error = driver->clazz->load_glyph( slot, face->size,
glyph_index,
@@ -1245,9 +1246,13 @@
/* destructor for sizes list */
static void
destroy_size( FT_Memory memory,
- FT_Size size,
- FT_Driver driver )
+ void* size_,
+ void* driver_ )
{
+ FT_Size size = (FT_Size)size_;
+ FT_Driver driver = (FT_Driver)driver_;
+
+
/* finalize client-specific data */
if ( size->generic.finalizer )
size->generic.finalizer( size );
@@ -1293,10 +1298,12 @@
/* destructor for faces list */
static void
destroy_face( FT_Memory memory,
- FT_Face face,
- FT_Driver driver )
+ void* face_,
+ void* driver_ )
{
- FT_Driver_Class clazz = driver->clazz;
+ FT_Face face = (FT_Face)face_;
+ FT_Driver driver = (FT_Driver)driver_;
+ FT_Driver_Class clazz = driver->clazz;
/* discard auto-hinting data */
@@ -1310,7 +1317,7 @@
/* discard all sizes for this face */
FT_List_Finalize( &face->sizes_list,
- (FT_List_Destructor)destroy_size,
+ destroy_size,
memory,
driver );
face->size = NULL;
@@ -1346,7 +1353,7 @@
Destroy_Driver( FT_Driver driver )
{
FT_List_Finalize( &driver->faces_list,
- (FT_List_Destructor)destroy_face,
+ destroy_face,
driver->root.memory,
driver );
}
diff --git a/thirdparty/freetype/src/base/ftoutln.c b/thirdparty/freetype/src/base/ftoutln.c
index 30ff21ff39..134f39d2b1 100644
--- a/thirdparty/freetype/src/base/ftoutln.c
+++ b/thirdparty/freetype/src/base/ftoutln.c
@@ -58,7 +58,9 @@
FT_Error error;
FT_Int n; /* index of contour in outline */
- FT_UInt first; /* index of first point in contour */
+ FT_Int first; /* index of first point in contour */
+ FT_Int last; /* index of last point in contour */
+
FT_Int tag; /* current point's state */
FT_Int shift;
@@ -73,18 +75,17 @@
shift = func_interface->shift;
delta = func_interface->delta;
- first = 0;
+ last = -1;
for ( n = 0; n < outline->n_contours; n++ )
{
- FT_Int last; /* index of last point in contour */
-
-
- FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n ));
+ FT_TRACE5(( "FT_Outline_Decompose: Contour %d\n", n ));
- last = outline->contours[n];
- if ( last < 0 )
+ first = last + 1;
+ last = outline->contours[n];
+ if ( last < first )
goto Invalid_Outline;
+
limit = outline->points + last;
v_start = outline->points[first];
@@ -282,8 +283,6 @@
Close:
if ( error )
goto Exit;
-
- first = (FT_UInt)last + 1;
}
FT_TRACE5(( "FT_Outline_Decompose: Done\n" ));
@@ -368,7 +367,7 @@
if ( n_points <= 0 || n_contours <= 0 )
goto Bad;
- end0 = end = -1;
+ end0 = -1;
for ( n = 0; n < n_contours; n++ )
{
end = outline->contours[n];
@@ -380,7 +379,7 @@
end0 = end;
}
- if ( end != n_points - 1 )
+ if ( end0 != n_points - 1 )
goto Bad;
/* XXX: check the tags array */
@@ -388,7 +387,7 @@
}
Bad:
- return FT_THROW( Invalid_Argument );
+ return FT_THROW( Invalid_Outline );
}
@@ -550,10 +549,12 @@
if ( !outline )
return;
- first = 0;
-
+ last = -1;
for ( n = 0; n < outline->n_contours; n++ )
{
+ /* keep the first contour point as is and swap points around it */
+ /* to guarantee that the cubic arches stay valid after reverse */
+ first = last + 2;
last = outline->contours[n];
/* reverse point table */
@@ -591,8 +592,6 @@
q--;
}
}
-
- first = last + 1;
}
outline->flags ^= FT_OUTLINE_REVERSE_FILL;
@@ -941,7 +940,7 @@
points = outline->points;
- first = 0;
+ last = -1;
for ( c = 0; c < outline->n_contours; c++ )
{
FT_Vector in, out, anchor, shift;
@@ -949,8 +948,9 @@
FT_Int i, j, k;
- l_in = 0;
- last = outline->contours[c];
+ first = last + 1;
+ last = outline->contours[c];
+ l_in = 0;
/* pacify compiler */
in.x = in.y = anchor.x = anchor.y = 0;
@@ -1037,8 +1037,6 @@
in = out;
l_in = l_out;
}
-
- first = last + 1;
}
return FT_Err_Ok;
@@ -1054,7 +1052,7 @@
FT_Int xshift, yshift;
FT_Vector* points;
FT_Vector v_prev, v_cur;
- FT_Int c, n, first;
+ FT_Int c, n, first, last;
FT_Pos area = 0;
@@ -1086,11 +1084,11 @@
points = outline->points;
- first = 0;
+ last = -1;
for ( c = 0; c < outline->n_contours; c++ )
{
- FT_Int last = outline->contours[c];
-
+ first = last + 1;
+ last = outline->contours[c];
v_prev.x = points[last].x >> xshift;
v_prev.y = points[last].y >> yshift;
@@ -1106,8 +1104,6 @@
v_prev = v_cur;
}
-
- first = last + 1;
}
if ( area > 0 )
diff --git a/thirdparty/freetype/src/base/ftstroke.c b/thirdparty/freetype/src/base/ftstroke.c
index db358e772e..92f1e43080 100644
--- a/thirdparty/freetype/src/base/ftstroke.c
+++ b/thirdparty/freetype/src/base/ftstroke.c
@@ -2055,7 +2055,9 @@
FT_Error error;
FT_Int n; /* index of contour in outline */
- FT_UInt first; /* index of first point in contour */
+ FT_Int first; /* index of first point in contour */
+ FT_Int last; /* index of last point in contour */
+
FT_Int tag; /* current point's state */
@@ -2067,22 +2069,17 @@
FT_Stroker_Rewind( stroker );
- first = 0;
-
+ last = -1;
for ( n = 0; n < outline->n_contours; n++ )
{
- FT_UInt last; /* index of last point in contour */
-
-
- last = (FT_UInt)outline->contours[n];
- limit = outline->points + last;
+ first = last + 1;
+ last = outline->contours[n];
/* skip empty points; we don't stroke these */
if ( last <= first )
- {
- first = last + 1;
continue;
- }
+
+ limit = outline->points + last;
v_start = outline->points[first];
v_last = outline->points[last];
@@ -2231,8 +2228,6 @@
if ( error )
goto Exit;
}
-
- first = last + 1;
}
return FT_Err_Ok;
diff --git a/thirdparty/freetype/src/base/ftsynth.c b/thirdparty/freetype/src/base/ftsynth.c
index 6ec25e13e4..f32edd3388 100644
--- a/thirdparty/freetype/src/base/ftsynth.c
+++ b/thirdparty/freetype/src/base/ftsynth.c
@@ -98,8 +98,17 @@
FT_EXPORT_DEF( void )
FT_GlyphSlot_Embolden( FT_GlyphSlot slot )
{
+ FT_GlyphSlot_AdjustWeight( slot, 0x0AAA, 0x0AAA );
+ }
+
+
+ FT_EXPORT_DEF( void )
+ FT_GlyphSlot_AdjustWeight( FT_GlyphSlot slot,
+ FT_Fixed xdelta,
+ FT_Fixed ydelta )
+ {
FT_Library library;
- FT_Face face;
+ FT_Size size;
FT_Error error;
FT_Pos xstr, ystr;
@@ -108,16 +117,15 @@
return;
library = slot->library;
- face = slot->face;
+ size = slot->face->size;
if ( slot->format != FT_GLYPH_FORMAT_OUTLINE &&
slot->format != FT_GLYPH_FORMAT_BITMAP )
return;
- /* some reasonable strength */
- xstr = FT_MulFix( face->units_per_EM,
- face->size->metrics.y_scale ) / 24;
- ystr = xstr;
+ /* express deltas in pixels in 26.6 format */
+ xstr = (FT_Pos)size->metrics.x_ppem * xdelta / 1024;
+ ystr = (FT_Pos)size->metrics.y_ppem * ydelta / 1024;
if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
FT_Outline_EmboldenXY( &slot->outline, xstr, ystr );
diff --git a/thirdparty/freetype/src/base/ftsystem.c b/thirdparty/freetype/src/base/ftsystem.c
index fcd289d19f..61c99e3635 100644
--- a/thirdparty/freetype/src/base/ftsystem.c
+++ b/thirdparty/freetype/src/base/ftsystem.c
@@ -206,7 +206,7 @@
* The number of bytes to read from the stream.
*
* @Return:
- * The number of bytes actually read. If `count' is zero (this is,
+ * The number of bytes actually read. If `count' is zero (that is,
* the function is used for seeking), a non-zero return value
* indicates an error.
*/
@@ -219,7 +219,7 @@
FT_FILE* file;
- if ( !count && offset > stream->size )
+ if ( offset > stream->size && !count )
return 1;
file = STREAM_FILE( stream );
@@ -227,6 +227,11 @@
if ( stream->pos != offset )
ft_fseek( file, (long)offset, SEEK_SET );
+ /* Avoid calling `fread` with `buffer=NULL` and `count=0`, */
+ /* which is undefined behaviour. */
+ if ( !count )
+ return 0;
+
return (unsigned long)ft_fread( buffer, 1, count, file );
}
diff --git a/thirdparty/freetype/src/bdf/bdf.h b/thirdparty/freetype/src/bdf/bdf.h
index 5acbd5f2f9..e2cb52c10a 100644
--- a/thirdparty/freetype/src/bdf/bdf.h
+++ b/thirdparty/freetype/src/bdf/bdf.h
@@ -240,10 +240,6 @@ FT_BEGIN_HEADER
bdf_free_font( bdf_font_t* font );
FT_LOCAL( bdf_property_t * )
- bdf_get_property( char* name,
- bdf_font_t* font );
-
- FT_LOCAL( bdf_property_t * )
bdf_get_font_property( bdf_font_t* font,
const char* name );
diff --git a/thirdparty/freetype/src/bdf/bdfdrivr.c b/thirdparty/freetype/src/bdf/bdfdrivr.c
index d7e8e0efc5..e02a160930 100644
--- a/thirdparty/freetype/src/bdf/bdfdrivr.c
+++ b/thirdparty/freetype/src/bdf/bdfdrivr.c
@@ -311,9 +311,9 @@ THE SOFTWARE.
FT_CALLBACK_DEF( void )
- BDF_Face_Done( FT_Face bdfface ) /* BDF_Face */
+ BDF_Face_Done( FT_Face face ) /* BDF_Face */
{
- BDF_Face face = (BDF_Face)bdfface;
+ BDF_Face bdfface = (BDF_Face)face;
FT_Memory memory;
@@ -322,31 +322,31 @@ THE SOFTWARE.
memory = FT_FACE_MEMORY( face );
- bdf_free_font( face->bdffont );
+ bdf_free_font( bdfface->bdffont );
- FT_FREE( face->en_table );
+ FT_FREE( bdfface->en_table );
- FT_FREE( face->charset_encoding );
- FT_FREE( face->charset_registry );
- FT_FREE( bdfface->family_name );
- FT_FREE( bdfface->style_name );
+ FT_FREE( bdfface->charset_encoding );
+ FT_FREE( bdfface->charset_registry );
+ FT_FREE( face->family_name );
+ FT_FREE( face->style_name );
- FT_FREE( bdfface->available_sizes );
+ FT_FREE( face->available_sizes );
- FT_FREE( face->bdffont );
+ FT_FREE( bdfface->bdffont );
}
FT_CALLBACK_DEF( FT_Error )
BDF_Face_Init( FT_Stream stream,
- FT_Face bdfface, /* BDF_Face */
+ FT_Face face, /* BDF_Face */
FT_Int face_index,
FT_Int num_params,
FT_Parameter* params )
{
- FT_Error error = FT_Err_Ok;
- BDF_Face face = (BDF_Face)bdfface;
- FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_Error error = FT_Err_Ok;
+ BDF_Face bdfface = (BDF_Face)face;
+ FT_Memory memory = FT_FACE_MEMORY( face );
bdf_font_t* font = NULL;
bdf_options_t options;
@@ -375,7 +375,7 @@ THE SOFTWARE.
goto Exit;
/* we have a bdf font: let's construct the face object */
- face->bdffont = font;
+ bdfface->bdffont = font;
/* BDF cannot have multiple faces in a single font file.
* XXX: non-zero face_index is already invalid argument, but
@@ -386,7 +386,7 @@ THE SOFTWARE.
if ( face_index > 0 && ( face_index & 0xFFFF ) > 0 )
{
FT_ERROR(( "BDF_Face_Init: invalid face index\n" ));
- BDF_Face_Done( bdfface );
+ BDF_Face_Done( face );
return FT_THROW( Invalid_Argument );
}
@@ -401,18 +401,18 @@ THE SOFTWARE.
font->unencoded_size,
font->unencoded_used ));
- bdfface->num_faces = 1;
- bdfface->face_index = 0;
+ face->num_faces = 1;
+ face->face_index = 0;
- bdfface->face_flags |= FT_FACE_FLAG_FIXED_SIZES |
- FT_FACE_FLAG_HORIZONTAL;
+ face->face_flags |= FT_FACE_FLAG_FIXED_SIZES |
+ FT_FACE_FLAG_HORIZONTAL;
prop = bdf_get_font_property( font, "SPACING" );
if ( prop && prop->format == BDF_ATOM &&
prop->value.atom &&
( *(prop->value.atom) == 'M' || *(prop->value.atom) == 'm' ||
*(prop->value.atom) == 'C' || *(prop->value.atom) == 'c' ) )
- bdfface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
+ face->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
/* FZ XXX: TO DO: FT_FACE_FLAGS_VERTICAL */
/* FZ XXX: I need a font to implement this */
@@ -420,26 +420,27 @@ THE SOFTWARE.
prop = bdf_get_font_property( font, "FAMILY_NAME" );
if ( prop && prop->value.atom )
{
- if ( FT_STRDUP( bdfface->family_name, prop->value.atom ) )
+ if ( FT_STRDUP( face->family_name, prop->value.atom ) )
goto Exit;
}
else
- bdfface->family_name = NULL;
+ face->family_name = NULL;
- if ( FT_SET_ERROR( bdf_interpret_style( face ) ) )
+ if ( FT_SET_ERROR( bdf_interpret_style( bdfface ) ) )
goto Exit;
/* the number of glyphs (with one slot for the undefined glyph */
/* at position 0 and all unencoded glyphs) */
- bdfface->num_glyphs = (FT_Long)( font->glyphs_size + 1 );
+ face->num_glyphs = (FT_Long)( font->glyphs_size + 1 );
- bdfface->num_fixed_sizes = 1;
- if ( FT_NEW( bdfface->available_sizes ) )
+ face->num_fixed_sizes = 1;
+ if ( FT_NEW( face->available_sizes ) )
goto Exit;
{
- FT_Bitmap_Size* bsize = bdfface->available_sizes;
- FT_Short resolution_x = 0, resolution_y = 0;
+ FT_Bitmap_Size* bsize = face->available_sizes;
+ FT_Short resolution_x = 0;
+ FT_Short resolution_y = 0;
long value;
@@ -598,20 +599,20 @@ THE SOFTWARE.
unsigned long n;
- if ( FT_QNEW_ARRAY( face->en_table, font->glyphs_size ) )
+ if ( FT_QNEW_ARRAY( bdfface->en_table, font->glyphs_size ) )
goto Exit;
- face->default_glyph = 0;
+ bdfface->default_glyph = 0;
for ( n = 0; n < font->glyphs_size; n++ )
{
- (face->en_table[n]).enc = cur[n].encoding;
+ (bdfface->en_table[n]).enc = cur[n].encoding;
FT_TRACE4(( " idx %ld, val 0x%lX\n", n, cur[n].encoding ));
- (face->en_table[n]).glyph = (FT_UShort)n;
+ (bdfface->en_table[n]).glyph = (FT_UShort)n;
if ( cur[n].encoding == font->default_char )
{
if ( n < FT_UINT_MAX )
- face->default_glyph = (FT_UInt)n;
+ bdfface->default_glyph = (FT_UInt)n;
else
FT_TRACE1(( "BDF_Face_Init:"
" idx %ld is too large for this system\n", n ));
@@ -639,27 +640,27 @@ THE SOFTWARE.
const char* s;
- if ( FT_STRDUP( face->charset_encoding,
+ if ( FT_STRDUP( bdfface->charset_encoding,
charset_encoding->value.atom ) ||
- FT_STRDUP( face->charset_registry,
+ FT_STRDUP( bdfface->charset_registry,
charset_registry->value.atom ) )
goto Exit;
/* Uh, oh, compare first letters manually to avoid dependency */
/* on locales. */
- s = face->charset_registry;
+ s = bdfface->charset_registry;
if ( ( s[0] == 'i' || s[0] == 'I' ) &&
( s[1] == 's' || s[1] == 'S' ) &&
( s[2] == 'o' || s[2] == 'O' ) )
{
s += 3;
- if ( !ft_strcmp( s, "10646" ) ||
- ( !ft_strcmp( s, "8859" ) &&
- !ft_strcmp( face->charset_encoding, "1" ) ) )
+ if ( !ft_strcmp( s, "10646" ) ||
+ ( !ft_strcmp( s, "8859" ) &&
+ !ft_strcmp( bdfface->charset_encoding, "1" ) ) )
unicode_charmap = 1;
/* another name for ASCII */
- else if ( !ft_strcmp( s, "646.1991" ) &&
- !ft_strcmp( face->charset_encoding, "IRV" ) )
+ else if ( !ft_strcmp( s, "646.1991" ) &&
+ !ft_strcmp( bdfface->charset_encoding, "IRV" ) )
unicode_charmap = 1;
}
@@ -667,7 +668,7 @@ THE SOFTWARE.
FT_CharMapRec charmap;
- charmap.face = FT_FACE( face );
+ charmap.face = face;
charmap.encoding = FT_ENCODING_NONE;
/* initial platform/encoding should indicate unset status? */
charmap.platform_id = TT_PLATFORM_APPLE_UNICODE;
@@ -693,7 +694,7 @@ THE SOFTWARE.
FT_CharMapRec charmap;
- charmap.face = FT_FACE( face );
+ charmap.face = face;
charmap.encoding = FT_ENCODING_ADOBE_STANDARD;
charmap.platform_id = TT_PLATFORM_ADOBE;
charmap.encoding_id = TT_ADOBE_ID_STANDARD;
@@ -701,8 +702,8 @@ THE SOFTWARE.
error = FT_CMap_New( &bdf_cmap_class, NULL, &charmap, NULL );
/* Select default charmap */
- if ( bdfface->num_charmaps )
- bdfface->charmap = bdfface->charmaps[0];
+ if ( face->num_charmaps )
+ face->charmap = face->charmaps[0];
}
}
}
@@ -711,7 +712,7 @@ THE SOFTWARE.
return error;
Fail:
- BDF_Face_Done( bdfface );
+ BDF_Face_Done( face );
return FT_THROW( Unknown_File_Format );
}
@@ -868,17 +869,18 @@ THE SOFTWARE.
*
*/
- static FT_Error
- bdf_get_bdf_property( BDF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ bdf_get_bdf_property( FT_Face face, /* BDF_Face */
const char* prop_name,
BDF_PropertyRec *aproperty )
{
+ BDF_Face bdfface = (BDF_Face)face;
bdf_property_t* prop;
- FT_ASSERT( face && face->bdffont );
+ FT_ASSERT( bdfface && bdfface->bdffont );
- prop = bdf_get_font_property( face->bdffont, prop_name );
+ prop = bdf_get_font_property( bdfface->bdffont, prop_name );
if ( prop )
{
switch ( prop->format )
@@ -921,13 +923,16 @@ THE SOFTWARE.
}
- static FT_Error
- bdf_get_charset_id( BDF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ bdf_get_charset_id( FT_Face face, /* BDF_Face */
const char* *acharset_encoding,
const char* *acharset_registry )
{
- *acharset_encoding = face->charset_encoding;
- *acharset_registry = face->charset_registry;
+ BDF_Face bdfface = (BDF_Face)face;
+
+
+ *acharset_encoding = bdfface->charset_encoding;
+ *acharset_registry = bdfface->charset_registry;
return 0;
}
@@ -964,7 +969,6 @@ THE SOFTWARE.
}
-
FT_CALLBACK_TABLE_DEF
const FT_Driver_ClassRec bdf_driver_class =
{
diff --git a/thirdparty/freetype/src/bdf/bdflib.c b/thirdparty/freetype/src/bdf/bdflib.c
index 2224698fc0..0fa7e0a8c5 100644
--- a/thirdparty/freetype/src/bdf/bdflib.c
+++ b/thirdparty/freetype/src/bdf/bdflib.c
@@ -51,6 +51,9 @@
#define FT_COMPONENT bdflib
+#define BUFSIZE 128
+
+
/**************************************************************************
*
* Default BDF font options.
@@ -378,7 +381,7 @@
*alen = 0;
if ( list == NULL || list->used == 0 )
- return 0;
+ return NULL;
dp = list->field[0];
for ( i = j = 0; i < list->used; i++ )
@@ -887,18 +890,18 @@
}
- FT_LOCAL_DEF( bdf_property_t* )
- bdf_get_property( char* name,
+ static bdf_property_t*
+ bdf_get_property( const char* name,
bdf_font_t* font )
{
size_t* propid;
if ( name == NULL || *name == 0 )
- return 0;
+ return NULL;
if ( ( propid = ft_hash_str_lookup( name, &(font->proptbl) ) ) == NULL )
- return 0;
+ return NULL;
if ( *propid >= num_bdf_properties_ )
return font->user_props + ( *propid - num_bdf_properties_ );
@@ -944,7 +947,7 @@
static FT_Error
bdf_add_comment_( bdf_font_t* font,
- char* comment,
+ const char* comment,
unsigned long len )
{
char* cp;
@@ -1053,27 +1056,24 @@
bdf_property_t* p;
- *name = sp = ep = line;
+ sp = ep = line;
while ( *ep && *ep != ' ' && *ep != '\t' )
ep++;
- hold = -1;
- if ( *ep )
- {
- hold = *ep;
- *ep = 0;
- }
+ hold = *ep;
+ *ep = '\0';
p = bdf_get_property( sp, font );
- /* Restore the character that was saved before any return can happen. */
- if ( hold != -1 )
- *ep = (char)hold;
-
/* If the property exists and is not an atom, just return here. */
if ( p && p->format != BDF_ATOM )
+ {
+ *ep = (char)hold; /* Undo NUL-termination. */
return 0;
+ }
+
+ *name = sp;
/* The property is an atom. Trim all leading and trailing whitespace */
/* and double quotes for the atom value. */
@@ -1081,25 +1081,26 @@
ep = line + linelen;
/* Trim the leading whitespace if it exists. */
- if ( *sp )
- *sp++ = 0;
- while ( *sp &&
- ( *sp == ' ' || *sp == '\t' ) )
- sp++;
+ if ( sp < ep )
+ do
+ sp++;
+ while ( *sp == ' ' || *sp == '\t' );
/* Trim the leading double quote if it exists. */
if ( *sp == '"' )
sp++;
+
*value = sp;
/* Trim the trailing whitespace if it exists. */
- while ( ep > sp &&
- ( *( ep - 1 ) == ' ' || *( ep - 1 ) == '\t' ) )
- *--ep = 0;
+ if ( sp < ep )
+ do
+ *ep-- = '\0';
+ while ( *ep == ' ' || *ep == '\t' );
/* Trim the trailing double quote if it exists. */
- if ( ep > sp && *( ep - 1 ) == '"' )
- *--ep = 0;
+ if ( *ep == '"' )
+ *ep = '\0';
return 1;
}
@@ -1775,7 +1776,7 @@
bdf_parse_t_* p;
char* name;
char* value;
- char nbuf[128];
+ char nbuf[BUFSIZE];
FT_Error error = FT_Err_Ok;
FT_UNUSED( lineno );
@@ -1796,7 +1797,7 @@
if ( bdf_get_font_property( p->font, "FONT_ASCENT" ) == 0 )
{
p->font->font_ascent = p->font->bbx.ascent;
- ft_sprintf( nbuf, "%hd", p->font->bbx.ascent );
+ ft_snprintf( nbuf, BUFSIZE, "%hd", p->font->bbx.ascent );
error = bdf_add_property_( p->font, "FONT_ASCENT",
nbuf, lineno );
if ( error )
@@ -1808,7 +1809,7 @@
if ( bdf_get_font_property( p->font, "FONT_DESCENT" ) == 0 )
{
p->font->font_descent = p->font->bbx.descent;
- ft_sprintf( nbuf, "%hd", p->font->bbx.descent );
+ ft_snprintf( nbuf, BUFSIZE, "%hd", p->font->bbx.descent );
error = bdf_add_property_( p->font, "FONT_DESCENT",
nbuf, lineno );
if ( error )
@@ -2116,7 +2117,7 @@
/* Check for the CHARS field -- font properties are optional */
if ( _bdf_strncmp( line, "CHARS", 5 ) == 0 )
{
- char nbuf[128];
+ char nbuf[BUFSIZE];
if ( !( p->flags & BDF_FONT_BBX_ ) )
@@ -2130,7 +2131,7 @@
/* Add the two standard X11 properties which are required */
/* for compiling fonts. */
p->font->font_ascent = p->font->bbx.ascent;
- ft_sprintf( nbuf, "%hd", p->font->bbx.ascent );
+ ft_snprintf( nbuf, BUFSIZE, "%hd", p->font->bbx.ascent );
error = bdf_add_property_( p->font, "FONT_ASCENT",
nbuf, lineno );
if ( error )
@@ -2138,7 +2139,7 @@
FT_TRACE2(( "bdf_parse_properties_: " ACMSG1, p->font->bbx.ascent ));
p->font->font_descent = p->font->bbx.descent;
- ft_sprintf( nbuf, "%hd", p->font->bbx.descent );
+ ft_snprintf( nbuf, BUFSIZE, "%hd", p->font->bbx.descent );
error = bdf_add_property_( p->font, "FONT_DESCENT",
nbuf, lineno );
if ( error )
diff --git a/thirdparty/freetype/src/bzip2/ftbzip2.c b/thirdparty/freetype/src/bzip2/ftbzip2.c
index 6cf10678b7..ad342bd011 100644
--- a/thirdparty/freetype/src/bzip2/ftbzip2.c
+++ b/thirdparty/freetype/src/bzip2/ftbzip2.c
@@ -62,10 +62,12 @@
static void*
- ft_bzip2_alloc( FT_Memory memory,
- int items,
- int size )
+ ft_bzip2_alloc( void* memory_, /* FT_Memory */
+ int items,
+ int size )
{
+ FT_Memory memory = (FT_Memory)memory_;
+
FT_ULong sz = (FT_ULong)size * (FT_ULong)items;
FT_Error error;
FT_Pointer p = NULL;
@@ -77,9 +79,12 @@
static void
- ft_bzip2_free( FT_Memory memory,
- void* address )
+ ft_bzip2_free( void* memory_, /* FT_Memory */
+ void* address )
{
+ FT_Memory memory = (FT_Memory)memory_;
+
+
FT_MEM_FREE( address );
}
@@ -170,8 +175,8 @@
}
/* initialize bzlib */
- bzstream->bzalloc = (alloc_func)ft_bzip2_alloc;
- bzstream->bzfree = (free_func) ft_bzip2_free;
+ bzstream->bzalloc = ft_bzip2_alloc;
+ bzstream->bzfree = ft_bzip2_free;
bzstream->opaque = zip->memory;
bzstream->avail_in = 0;
diff --git a/thirdparty/freetype/src/cache/ftcbasic.c b/thirdparty/freetype/src/cache/ftcbasic.c
index 4c6d41b2cd..24a56c8d26 100644
--- a/thirdparty/freetype/src/cache/ftcbasic.c
+++ b/thirdparty/freetype/src/cache/ftcbasic.c
@@ -337,7 +337,7 @@
#if 1 /* inlining is about 50% faster! */
FTC_GCACHE_LOOKUP_CMP( cache,
ftc_basic_family_compare,
- FTC_GNode_Compare,
+ ftc_gnode_compare,
hash, gindex,
&query,
node,
@@ -411,7 +411,7 @@
FTC_GCACHE_LOOKUP_CMP( cache,
ftc_basic_family_compare,
- FTC_GNode_Compare,
+ ftc_gnode_compare,
hash, gindex,
&query,
node,
@@ -537,7 +537,7 @@
#if 1 /* inlining is about 50% faster! */
FTC_GCACHE_LOOKUP_CMP( cache,
ftc_basic_family_compare,
- FTC_SNode_Compare,
+ ftc_snode_compare,
hash, gindex,
&query,
node,
@@ -613,7 +613,7 @@
FTC_GCACHE_LOOKUP_CMP( cache,
ftc_basic_family_compare,
- FTC_SNode_Compare,
+ ftc_snode_compare,
hash, gindex,
&query,
node,
diff --git a/thirdparty/freetype/src/cache/ftccache.c b/thirdparty/freetype/src/cache/ftccache.c
index d54e68ca9a..e0698557b7 100644
--- a/thirdparty/freetype/src/cache/ftccache.c
+++ b/thirdparty/freetype/src/cache/ftccache.c
@@ -94,8 +94,8 @@
idx = hash & cache->mask;
- if ( idx < cache->p )
- idx = hash & ( 2 * cache->mask + 1 );
+ if ( idx >= cache->p )
+ idx = hash & ( cache->mask >> 1 );
return cache->buckets + idx;
}
@@ -113,9 +113,9 @@
for (;;)
{
FTC_Node node, *pnode;
- FT_UFast p = cache->p;
- FT_UFast mask = cache->mask;
- FT_UFast count = mask + p + 1; /* number of buckets */
+ FT_UFast p = cache->p;
+ FT_UFast size = cache->mask + 1; /* available size */
+ FT_UFast half = size >> 1;
/* do we need to expand the buckets array? */
@@ -127,20 +127,22 @@
/* try to expand the buckets array _before_ splitting
* the bucket lists
*/
- if ( p >= mask )
+ if ( p == size )
{
FT_Memory memory = cache->memory;
FT_Error error;
/* if we can't expand the array, leave immediately */
- if ( FT_RENEW_ARRAY( cache->buckets,
- ( mask + 1 ) * 2, ( mask + 1 ) * 4 ) )
+ if ( FT_QRENEW_ARRAY( cache->buckets, size, size * 2 ) )
break;
+
+ cache->mask = 2 * size - 1;
+ half = size;
}
- /* split a single bucket */
- pnode = cache->buckets + p;
+ /* the bucket to split */
+ pnode = cache->buckets + p - half;
for (;;)
{
@@ -148,7 +150,7 @@
if ( !node )
break;
- if ( node->hash & ( mask + 1 ) )
+ if ( node->hash & half )
{
*pnode = node->link;
node->link = new_list;
@@ -158,56 +160,50 @@
pnode = &node->link;
}
- cache->buckets[p + mask + 1] = new_list;
+ cache->buckets[p] = new_list;
cache->slack += FTC_HASH_MAX_LOAD;
+ cache->p = p + 1;
- if ( p >= mask )
- {
- cache->mask = 2 * mask + 1;
- cache->p = 0;
- }
- else
- cache->p = p + 1;
+ FT_TRACE2(( "ftc_cache_resize: cache %u increased to %u hashes\n",
+ cache->index, cache->p ));
}
/* do we need to shrink the buckets array? */
- else if ( cache->slack > (FT_Long)count * FTC_HASH_SUB_LOAD )
+ else if ( cache->slack > (FT_Long)p * FTC_HASH_SUB_LOAD )
{
- FT_UFast old_index = p + mask;
- FTC_Node* pold;
+ FTC_Node old_list = cache->buckets[--p];
- if ( old_index + 1 <= FTC_HASH_INITIAL_SIZE )
+ if ( p < FTC_HASH_INITIAL_SIZE )
break;
- if ( p == 0 )
+ if ( p == half )
{
FT_Memory memory = cache->memory;
FT_Error error;
/* if we can't shrink the array, leave immediately */
- if ( FT_QRENEW_ARRAY( cache->buckets,
- ( mask + 1 ) * 2, mask + 1 ) )
+ if ( FT_QRENEW_ARRAY( cache->buckets, size, half ) )
break;
- cache->mask >>= 1;
- p = cache->mask;
+ cache->mask = half - 1;
}
- else
- p--;
- pnode = cache->buckets + p;
+ /* the bucket to merge */
+ pnode = cache->buckets + p - half;
+
while ( *pnode )
pnode = &(*pnode)->link;
- pold = cache->buckets + old_index;
- *pnode = *pold;
- *pold = NULL;
+ *pnode = old_list;
cache->slack -= FTC_HASH_MAX_LOAD;
cache->p = p;
+
+ FT_TRACE2(( "ftc_cache_resize: cache %u decreased to %u hashes\n",
+ cache->index, cache->p ));
}
/* otherwise, the hash table is balanced */
@@ -239,7 +235,7 @@
if ( node == node0 )
break;
- pnode = &(*pnode)->link;
+ pnode = &node->link;
}
*pnode = node0->link;
@@ -323,40 +319,41 @@
FT_LOCAL_DEF( FT_Error )
- FTC_Cache_Init( FTC_Cache cache )
- {
- return ftc_cache_init( cache );
- }
-
-
- FT_LOCAL_DEF( FT_Error )
ftc_cache_init( FTC_Cache cache )
{
FT_Memory memory = cache->memory;
FT_Error error;
- cache->p = 0;
+ cache->p = FTC_HASH_INITIAL_SIZE;
cache->mask = FTC_HASH_INITIAL_SIZE - 1;
cache->slack = FTC_HASH_INITIAL_SIZE * FTC_HASH_MAX_LOAD;
- FT_MEM_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE * 2 );
+ FT_MEM_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE );
return error;
}
- static void
- FTC_Cache_Clear( FTC_Cache cache )
+ FT_LOCAL_DEF( FT_Error )
+ FTC_Cache_Init( FTC_Cache cache )
{
- if ( cache && cache->buckets )
+ return ftc_cache_init( cache );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ftc_cache_done( FTC_Cache cache )
+ {
+ FT_Memory memory = cache->memory;
+
+
+ if ( cache->buckets )
{
FTC_Manager manager = cache->manager;
+ FT_UFast count = cache->p;
FT_UFast i;
- FT_UFast count;
- count = cache->p + cache->mask + 1;
-
for ( i = 0; i < count; i++ )
{
FTC_Node node = cache->buckets[i], next;
@@ -376,30 +373,14 @@
cache->clazz.node_free( node, cache );
node = next;
}
- cache->buckets[i] = NULL;
}
- ftc_cache_resize( cache );
}
- }
+ FT_FREE( cache->buckets );
- FT_LOCAL_DEF( void )
- ftc_cache_done( FTC_Cache cache )
- {
- if ( cache->memory )
- {
- FT_Memory memory = cache->memory;
-
-
- FTC_Cache_Clear( cache );
-
- FT_FREE( cache->buckets );
- cache->mask = 0;
- cache->p = 0;
- cache->slack = 0;
-
- cache->memory = NULL;
- }
+ cache->p = 0;
+ cache->mask = 0;
+ cache->slack = 0;
}
@@ -562,12 +543,12 @@
FTC_Cache_RemoveFaceID( FTC_Cache cache,
FTC_FaceID face_id )
{
- FT_UFast i, count;
FTC_Manager manager = cache->manager;
FTC_Node frees = NULL;
+ FT_UFast count = cache->p;
+ FT_UFast i;
- count = cache->p + cache->mask + 1;
for ( i = 0; i < count; i++ )
{
FTC_Node* pnode = cache->buckets + i;
diff --git a/thirdparty/freetype/src/cache/ftccache.h b/thirdparty/freetype/src/cache/ftccache.h
index 23bcb65858..850d2554b5 100644
--- a/thirdparty/freetype/src/cache/ftccache.h
+++ b/thirdparty/freetype/src/cache/ftccache.h
@@ -72,11 +72,12 @@ FT_BEGIN_HEADER
#define FTC_NODE_NEXT( x ) FTC_NODE( (x)->mru.next )
#define FTC_NODE_PREV( x ) FTC_NODE( (x)->mru.prev )
+ /* address the hash table entries */
#ifdef FTC_INLINE
-#define FTC_NODE_TOP_FOR_HASH( cache, hash ) \
- ( ( cache )->buckets + \
- ( ( ( ( hash ) & ( cache )->mask ) < ( cache )->p ) \
- ? ( ( hash ) & ( ( cache )->mask * 2 + 1 ) ) \
+#define FTC_NODE_TOP_FOR_HASH( cache, hash ) \
+ ( ( cache )->buckets + \
+ ( ( ( ( hash ) & ( cache )->mask ) >= ( cache )->p ) \
+ ? ( ( hash ) & ( ( cache )->mask >> 1 ) ) \
: ( ( hash ) & ( cache )->mask ) ) )
#else
FT_LOCAL( FTC_Node* )
@@ -139,11 +140,13 @@ FT_BEGIN_HEADER
} FTC_CacheClassRec;
- /* each cache really implements a dynamic hash table to manage its nodes */
+ /* each cache really implements a hash table to manage its nodes */
+ /* the number of the table entries (buckets) can change dynamically */
+ /* each bucket contains a linked lists of nodes for a given hash */
typedef struct FTC_CacheRec_
{
- FT_UFast p;
- FT_UFast mask;
+ FT_UFast p; /* hash table counter */
+ FT_UFast mask; /* hash table index range */
FT_Long slack;
FTC_Node* buckets;
diff --git a/thirdparty/freetype/src/cache/ftcglyph.c b/thirdparty/freetype/src/cache/ftcglyph.c
index b3fb2f219c..d344733f37 100644
--- a/thirdparty/freetype/src/cache/ftcglyph.c
+++ b/thirdparty/freetype/src/cache/ftcglyph.c
@@ -79,20 +79,6 @@
}
-#ifdef FTC_INLINE
-
- FT_LOCAL_DEF( FT_Bool )
- FTC_GNode_Compare( FTC_GNode gnode,
- FTC_GQuery gquery,
- FTC_Cache cache,
- FT_Bool* list_changed )
- {
- return ftc_gnode_compare( FTC_NODE( gnode ), gquery,
- cache, list_changed );
- }
-
-#endif
-
/*************************************************************************/
/*************************************************************************/
/***** *****/
@@ -115,22 +101,22 @@
FT_LOCAL_DEF( FT_Error )
- ftc_gcache_init( FTC_Cache ftccache )
+ ftc_gcache_init( FTC_Cache cache )
{
- FTC_GCache cache = (FTC_GCache)ftccache;
+ FTC_GCache gcache = (FTC_GCache)cache;
FT_Error error;
- error = FTC_Cache_Init( FTC_CACHE( cache ) );
+ error = FTC_Cache_Init( cache );
if ( !error )
{
- FTC_GCacheClass clazz = (FTC_GCacheClass)FTC_CACHE( cache )->org_class;
+ FTC_GCacheClass clazz = (FTC_GCacheClass)cache->org_class;
- FTC_MruList_Init( &cache->families,
+ FTC_MruList_Init( &gcache->families,
clazz->family_class,
0, /* no maximum here! */
cache,
- FTC_CACHE( cache )->memory );
+ cache->memory );
}
return error;
@@ -140,31 +126,31 @@
#if 0
FT_LOCAL_DEF( FT_Error )
- FTC_GCache_Init( FTC_GCache cache )
+ FTC_GCache_Init( FTC_GCache gcache )
{
- return ftc_gcache_init( FTC_CACHE( cache ) );
+ return ftc_gcache_init( FTC_CACHE( gcache ) );
}
#endif /* 0 */
FT_LOCAL_DEF( void )
- ftc_gcache_done( FTC_Cache ftccache )
+ ftc_gcache_done( FTC_Cache cache )
{
- FTC_GCache cache = (FTC_GCache)ftccache;
+ FTC_GCache gcache = (FTC_GCache)cache;
- FTC_Cache_Done( (FTC_Cache)cache );
- FTC_MruList_Done( &cache->families );
+ FTC_Cache_Done( cache );
+ FTC_MruList_Done( &gcache->families );
}
#if 0
FT_LOCAL_DEF( void )
- FTC_GCache_Done( FTC_GCache cache )
+ FTC_GCache_Done( FTC_GCache gcache )
{
- ftc_gcache_done( FTC_CACHE( cache ) );
+ ftc_gcache_done( FTC_CACHE( gcache ) );
}
#endif /* 0 */
@@ -183,7 +169,7 @@
#ifndef FTC_INLINE
FT_LOCAL_DEF( FT_Error )
- FTC_GCache_Lookup( FTC_GCache cache,
+ FTC_GCache_Lookup( FTC_GCache gcache,
FT_Offset hash,
FT_UInt gindex,
FTC_GQuery query,
@@ -204,7 +190,7 @@
/* out-of-memory condition occurs during glyph node initialization. */
family->num_nodes++;
- error = FTC_Cache_Lookup( FTC_CACHE( cache ), hash, query, anode );
+ error = FTC_Cache_Lookup( FTC_CACHE( gcache ), hash, query, anode );
if ( --family->num_nodes == 0 )
FTC_FAMILY_FREE( family, cache );
diff --git a/thirdparty/freetype/src/cache/ftcglyph.h b/thirdparty/freetype/src/cache/ftcglyph.h
index 728d4db1d6..0181e98166 100644
--- a/thirdparty/freetype/src/cache/ftcglyph.h
+++ b/thirdparty/freetype/src/cache/ftcglyph.h
@@ -58,7 +58,7 @@
* - FTC_GNode sub-class, e.g. MyNode, with relevant methods:
* my_node_new (must call FTC_GNode_Init)
* my_node_free (must call FTC_GNode_Done)
- * my_node_compare (must call FTC_GNode_Compare)
+ * my_node_compare (must call ftc_gnode_compare)
* my_node_remove_faceid (must call ftc_gnode_unselect in case
* of match)
*
@@ -179,19 +179,6 @@ FT_BEGIN_HEADER
FT_UInt gindex, /* glyph index for node */
FTC_Family family );
-#ifdef FTC_INLINE
-
- /* returns TRUE iff the query's glyph index correspond to the node; */
- /* this assumes that the `family' and `hash' fields of the query are */
- /* already correctly set */
- FT_LOCAL( FT_Bool )
- FTC_GNode_Compare( FTC_GNode gnode,
- FTC_GQuery gquery,
- FTC_Cache cache,
- FT_Bool* list_changed );
-
-#endif
-
/* call this function to clear a node's family -- this is necessary */
/* to implement the `node_remove_faceid' cache method correctly */
FT_LOCAL( void )
diff --git a/thirdparty/freetype/src/cache/ftcmanag.c b/thirdparty/freetype/src/cache/ftcmanag.c
index 6c84339100..94f8469c92 100644
--- a/thirdparty/freetype/src/cache/ftcmanag.c
+++ b/thirdparty/freetype/src/cache/ftcmanag.c
@@ -426,7 +426,7 @@
memory = manager->memory;
/* now discard all caches */
- for (idx = manager->num_caches; idx-- > 0; )
+ for ( idx = manager->num_caches; idx-- > 0; )
{
FTC_Cache cache = manager->caches[idx];
@@ -537,7 +537,7 @@
FT_LOCAL_DEF( void )
FTC_Manager_Compress( FTC_Manager manager )
{
- FTC_Node node, first;
+ FTC_Node node, prev, first;
if ( !manager )
@@ -557,20 +557,16 @@
return;
/* go to last node -- it's a circular list */
- node = FTC_NODE_PREV( first );
+ prev = FTC_NODE_PREV( first );
do
{
- FTC_Node prev;
-
-
- prev = ( node == first ) ? NULL : FTC_NODE_PREV( node );
+ node = prev;
+ prev = FTC_NODE_PREV( node );
if ( node->ref_count <= 0 )
ftc_node_destroy( node, manager );
- node = prev;
-
- } while ( node && manager->cur_weight > manager->max_weight );
+ } while ( node != first && manager->cur_weight > manager->max_weight );
}
@@ -633,20 +629,20 @@
FT_UInt count )
{
FTC_Node first = manager->nodes_list;
- FTC_Node node;
- FT_UInt result;
+ FTC_Node prev, node;
+ FT_UInt result = 0;
/* try to remove `count' nodes from the list */
- if ( !first ) /* empty list! */
- return 0;
+ if ( !first || !count )
+ return result;
- /* go to last node - it's a circular list */
- node = FTC_NODE_PREV(first);
- for ( result = 0; result < count; )
+ /* go to last node -- it's a circular list */
+ prev = FTC_NODE_PREV( first );
+ do
{
- FTC_Node prev = FTC_NODE_PREV( node );
-
+ node = prev;
+ prev = FTC_NODE_PREV( node );
/* don't touch locked nodes */
if ( node->ref_count <= 0 )
@@ -654,13 +650,9 @@
ftc_node_destroy( node, manager );
result++;
}
+ } while ( node != first && result < count );
- if ( node == first )
- break;
-
- node = prev;
- }
- return result;
+ return result;
}
diff --git a/thirdparty/freetype/src/cache/ftcmru.c b/thirdparty/freetype/src/cache/ftcmru.c
index 67227033e7..ad10a06bc4 100644
--- a/thirdparty/freetype/src/cache/ftcmru.c
+++ b/thirdparty/freetype/src/cache/ftcmru.c
@@ -329,29 +329,23 @@
FTC_MruNode_CompareFunc selection,
FT_Pointer key )
{
- FTC_MruNode first, node, next;
+ FTC_MruNode first = list->nodes;
+ FTC_MruNode prev, node;
- first = list->nodes;
- while ( first && ( !selection || selection( first, key ) ) )
- {
- FTC_MruList_Remove( list, first );
- first = list->nodes;
- }
+ if ( !first || !selection )
+ return;
- if ( first )
+ prev = first->prev;
+ do
{
- node = first->next;
- while ( node != first )
- {
- next = node->next;
+ node = prev;
+ prev = node->prev;
- if ( selection( node, key ) )
- FTC_MruList_Remove( list, node );
+ if ( selection( node, key ) )
+ FTC_MruList_Remove( list, node );
- node = next;
- }
- }
+ } while ( node != first );
}
diff --git a/thirdparty/freetype/src/cache/ftcsbits.c b/thirdparty/freetype/src/cache/ftcsbits.c
index ee9dab2632..9929a0bcc3 100644
--- a/thirdparty/freetype/src/cache/ftcsbits.c
+++ b/thirdparty/freetype/src/cache/ftcsbits.c
@@ -342,7 +342,7 @@
FT_Bool result;
- if (list_changed)
+ if ( list_changed )
*list_changed = FALSE;
result = FT_BOOL( gnode->family == gquery->family &&
gindex - gnode->gindex < snode->count );
@@ -411,19 +411,4 @@
return result;
}
-
-#ifdef FTC_INLINE
-
- FT_LOCAL_DEF( FT_Bool )
- FTC_SNode_Compare( FTC_SNode snode,
- FTC_GQuery gquery,
- FTC_Cache cache,
- FT_Bool* list_changed )
- {
- return ftc_snode_compare( FTC_NODE( snode ), gquery,
- cache, list_changed );
- }
-
-#endif
-
/* END */
diff --git a/thirdparty/freetype/src/cache/ftcsbits.h b/thirdparty/freetype/src/cache/ftcsbits.h
index 3473923f03..e833cb5c30 100644
--- a/thirdparty/freetype/src/cache/ftcsbits.h
+++ b/thirdparty/freetype/src/cache/ftcsbits.h
@@ -81,17 +81,6 @@ FT_BEGIN_HEADER
FTC_SNode_Weight( FTC_SNode inode );
#endif
-
-#ifdef FTC_INLINE
-
- FT_LOCAL( FT_Bool )
- FTC_SNode_Compare( FTC_SNode snode,
- FTC_GQuery gquery,
- FTC_Cache cache,
- FT_Bool* list_changed);
-
-#endif
-
/* */
FT_END_HEADER
diff --git a/thirdparty/freetype/src/cff/cffcmap.c b/thirdparty/freetype/src/cff/cffcmap.c
index 6ed3143222..10d287bc81 100644
--- a/thirdparty/freetype/src/cff/cffcmap.c
+++ b/thirdparty/freetype/src/cff/cffcmap.c
@@ -32,9 +32,10 @@
/*************************************************************************/
FT_CALLBACK_DEF( FT_Error )
- cff_cmap_encoding_init( CFF_CMapStd cmap,
- FT_Pointer pointer )
+ cff_cmap_encoding_init( FT_CMap cmap,
+ FT_Pointer pointer )
{
+ CFF_CMapStd cffcmap = (CFF_CMapStd)cmap;
TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
CFF_Font cff = (CFF_Font)face->extra.data;
CFF_Encoding encoding = &cff->encoding;
@@ -42,63 +43,56 @@
FT_UNUSED( pointer );
- cmap->gids = encoding->codes;
+ cffcmap->gids = encoding->codes;
return 0;
}
FT_CALLBACK_DEF( void )
- cff_cmap_encoding_done( CFF_CMapStd cmap )
+ cff_cmap_encoding_done( FT_CMap cmap )
{
- cmap->gids = NULL;
+ CFF_CMapStd cffcmap = (CFF_CMapStd)cmap;
+
+
+ cffcmap->gids = NULL;
}
FT_CALLBACK_DEF( FT_UInt )
- cff_cmap_encoding_char_index( CFF_CMapStd cmap,
- FT_UInt32 char_code )
+ cff_cmap_encoding_char_index( FT_CMap cmap,
+ FT_UInt32 char_code )
{
- FT_UInt result = 0;
+ CFF_CMapStd cffcmap = (CFF_CMapStd)cmap;
+ FT_UInt result = 0;
if ( char_code < 256 )
- result = cmap->gids[char_code];
+ result = cffcmap->gids[char_code];
return result;
}
- FT_CALLBACK_DEF( FT_UInt32 )
- cff_cmap_encoding_char_next( CFF_CMapStd cmap,
- FT_UInt32 *pchar_code )
+ FT_CALLBACK_DEF( FT_UInt )
+ cff_cmap_encoding_char_next( FT_CMap cmap,
+ FT_UInt32 *pchar_code )
{
- FT_UInt result = 0;
- FT_UInt32 char_code = *pchar_code;
+ CFF_CMapStd cffcmap = (CFF_CMapStd)cmap;
+ FT_UInt result = 0;
+ FT_UInt32 char_code = *pchar_code;
- *pchar_code = 0;
-
- if ( char_code < 255 )
+ while ( char_code < 255 )
{
- FT_UInt code = (FT_UInt)( char_code + 1 );
-
-
- for (;;)
+ result = cffcmap->gids[++char_code];
+ if ( result )
{
- if ( code >= 256 )
- break;
-
- result = cmap->gids[code];
- if ( result != 0 )
- {
- *pchar_code = code;
- break;
- }
-
- code++;
+ *pchar_code = char_code;
+ break;
}
}
+
return result;
}
@@ -130,9 +124,10 @@
/*************************************************************************/
FT_CALLBACK_DEF( const char* )
- cff_sid_to_glyph_name( TT_Face face,
+ cff_sid_to_glyph_name( void* face_, /* TT_Face */
FT_UInt idx )
{
+ TT_Face face = (TT_Face)face_;
CFF_Font cff = (CFF_Font)face->extra.data;
CFF_Charset charset = &cff->charset;
FT_UInt sid = charset->sids[idx];
@@ -143,14 +138,15 @@
FT_CALLBACK_DEF( FT_Error )
- cff_cmap_unicode_init( PS_Unicodes unicodes,
+ cff_cmap_unicode_init( FT_CMap cmap, /* PS_Unicodes */
FT_Pointer pointer )
{
- TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
- FT_Memory memory = FT_FACE_MEMORY( face );
- CFF_Font cff = (CFF_Font)face->extra.data;
- CFF_Charset charset = &cff->charset;
- FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames;
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ CFF_Font cff = (CFF_Font)face->extra.data;
+ CFF_Charset charset = &cff->charset;
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames;
FT_UNUSED( pointer );
@@ -166,17 +162,18 @@
return psnames->unicodes_init( memory,
unicodes,
cff->num_glyphs,
- (PS_GetGlyphNameFunc)&cff_sid_to_glyph_name,
+ &cff_sid_to_glyph_name,
(PS_FreeGlyphNameFunc)NULL,
(FT_Pointer)face );
}
FT_CALLBACK_DEF( void )
- cff_cmap_unicode_done( PS_Unicodes unicodes )
+ cff_cmap_unicode_done( FT_CMap cmap ) /* PS_Unicodes */
{
- FT_Face face = FT_CMAP_FACE( unicodes );
- FT_Memory memory = FT_FACE_MEMORY( face );
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ FT_Face face = FT_CMAP_FACE( cmap );
+ FT_Memory memory = FT_FACE_MEMORY( face );
FT_FREE( unicodes->maps );
@@ -185,25 +182,27 @@
FT_CALLBACK_DEF( FT_UInt )
- cff_cmap_unicode_char_index( PS_Unicodes unicodes,
- FT_UInt32 char_code )
+ cff_cmap_unicode_char_index( FT_CMap cmap, /* PS_Unicodes */
+ FT_UInt32 char_code )
{
- TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
- CFF_Font cff = (CFF_Font)face->extra.data;
- FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames;
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
+ CFF_Font cff = (CFF_Font)face->extra.data;
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames;
return psnames->unicodes_char_index( unicodes, char_code );
}
- FT_CALLBACK_DEF( FT_UInt32 )
- cff_cmap_unicode_char_next( PS_Unicodes unicodes,
- FT_UInt32 *pchar_code )
+ FT_CALLBACK_DEF( FT_UInt )
+ cff_cmap_unicode_char_next( FT_CMap cmap, /* PS_Unicodes */
+ FT_UInt32 *pchar_code )
{
- TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
- CFF_Font cff = (CFF_Font)face->extra.data;
- FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames;
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
+ CFF_Font cff = (CFF_Font)face->extra.data;
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames;
return psnames->unicodes_char_next( unicodes, pchar_code );
diff --git a/thirdparty/freetype/src/cff/cffdrivr.c b/thirdparty/freetype/src/cff/cffdrivr.c
index 4e2e0e00de..9898d625ca 100644
--- a/thirdparty/freetype/src/cff/cffdrivr.c
+++ b/thirdparty/freetype/src/cff/cffdrivr.c
@@ -108,20 +108,20 @@
* They can be implemented by format-specific interfaces.
*/
FT_CALLBACK_DEF( FT_Error )
- cff_get_kerning( FT_Face ttface, /* TT_Face */
+ cff_get_kerning( FT_Face face, /* CFF_Face */
FT_UInt left_glyph,
FT_UInt right_glyph,
FT_Vector* kerning )
{
- TT_Face face = (TT_Face)ttface;
- SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+ CFF_Face cffface = (CFF_Face)face;
+ SFNT_Service sfnt = (SFNT_Service)cffface->sfnt;
kerning->x = 0;
kerning->y = 0;
if ( sfnt )
- kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph );
+ kerning->x = sfnt->get_kerning( cffface, left_glyph, right_glyph );
return FT_Err_Ok;
}
@@ -158,23 +158,23 @@
* FreeType error code. 0 means success.
*/
FT_CALLBACK_DEF( FT_Error )
- cff_glyph_load( FT_GlyphSlot cffslot, /* CFF_GlyphSlot */
- FT_Size cffsize, /* CFF_Size */
+ cff_glyph_load( FT_GlyphSlot slot, /* CFF_GlyphSlot */
+ FT_Size size, /* CFF_Size */
FT_UInt glyph_index,
FT_Int32 load_flags )
{
FT_Error error;
- CFF_GlyphSlot slot = (CFF_GlyphSlot)cffslot;
- CFF_Size size = (CFF_Size)cffsize;
+ CFF_GlyphSlot cffslot = (CFF_GlyphSlot)slot;
+ CFF_Size cffsize = (CFF_Size)size;
- if ( !slot )
+ if ( !cffslot )
return FT_THROW( Invalid_Slot_Handle );
FT_TRACE1(( "cff_glyph_load: glyph index %d\n", glyph_index ));
/* check whether we want a scaled outline or bitmap */
- if ( !size )
+ if ( !cffsize )
load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING;
/* reset the size object if necessary */
@@ -184,12 +184,12 @@
if ( size )
{
/* these two objects must have the same parent */
- if ( cffsize->face != cffslot->face )
+ if ( size->face != slot->face )
return FT_THROW( Invalid_Face_Handle );
}
/* now load the glyph outline if necessary */
- error = cff_slot_load( slot, size, glyph_index, load_flags );
+ error = cff_slot_load( cffslot, cffsize, glyph_index, load_flags );
/* force drop-out mode to 2 - irrelevant now */
/* slot->outline.dropout_mode = 2; */
@@ -216,7 +216,7 @@
/* it is no longer necessary that those values are identical to */
/* the values in the `CFF' table */
- TT_Face ttface = (TT_Face)face;
+ CFF_Face cffface = (CFF_Face)face;
FT_Short dummy;
@@ -225,7 +225,7 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
/* no fast retrieval for blended MM fonts without VVAR table */
if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) &&
- !( ttface->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
+ !( cffface->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
return FT_THROW( Unimplemented_Feature );
#endif
@@ -233,7 +233,7 @@
/* otherwise we extract the info from the CFF glyphstrings */
/* (instead of synthesizing a global value using the `OS/2' */
/* table) */
- if ( !ttface->vertical_info )
+ if ( !cffface->vertical_info )
goto Missing_Table;
for ( nn = 0; nn < count; nn++ )
@@ -241,11 +241,11 @@
FT_UShort ah;
- ( (SFNT_Service)ttface->sfnt )->get_metrics( ttface,
- 1,
- start + nn,
- &dummy,
- &ah );
+ ( (SFNT_Service)cffface->sfnt )->get_metrics( cffface,
+ 1,
+ start + nn,
+ &dummy,
+ &ah );
FT_TRACE5(( " idx %d: advance height %d font unit%s\n",
start + nn,
@@ -259,12 +259,12 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
/* no fast retrieval for blended MM fonts without HVAR table */
if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) &&
- !( ttface->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
+ !( cffface->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
return FT_THROW( Unimplemented_Feature );
#endif
/* check whether we have data from the `hmtx' table at all */
- if ( !ttface->horizontal.number_Of_HMetrics )
+ if ( !cffface->horizontal.number_Of_HMetrics )
goto Missing_Table;
for ( nn = 0; nn < count; nn++ )
@@ -272,11 +272,11 @@
FT_UShort aw;
- ( (SFNT_Service)ttface->sfnt )->get_metrics( ttface,
- 0,
- start + nn,
- &dummy,
- &aw );
+ ( (SFNT_Service)cffface->sfnt )->get_metrics( cffface,
+ 0,
+ start + nn,
+ &dummy,
+ &aw );
FT_TRACE5(( " idx %d: advance width %d font unit%s\n",
start + nn,
@@ -312,13 +312,14 @@
*
*/
- static FT_Error
- cff_get_glyph_name( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_get_glyph_name( FT_Face face, /* CFF_Face */
FT_UInt glyph_index,
FT_Pointer buffer,
FT_UInt buffer_max )
{
- CFF_Font font = (CFF_Font)face->extra.data;
+ CFF_Face cffface = (CFF_Face)face;
+ CFF_Font font = (CFF_Font)cffface->extra.data;
FT_String* gname;
FT_UShort sid;
FT_Error error;
@@ -338,10 +339,7 @@
if ( service && service->get_name )
- return service->get_name( FT_FACE( face ),
- glyph_index,
- buffer,
- buffer_max );
+ return service->get_name( face, glyph_index, buffer, buffer_max );
else
{
FT_ERROR(( "cff_get_glyph_name:"
@@ -366,7 +364,7 @@
/* first, locate the sid in the charset table */
sid = font->charset.sids[glyph_index];
- /* now, lookup the name itself */
+ /* now, look up the name itself */
gname = cff_index_get_sid_string( font, sid );
if ( gname )
@@ -379,21 +377,19 @@
}
- static FT_UInt
- cff_get_name_index( CFF_Face face,
+ FT_CALLBACK_DEF( FT_UInt )
+ cff_get_name_index( FT_Face face, /* CFF_Face */
const FT_String* glyph_name )
{
- CFF_Font cff;
- CFF_Charset charset;
+ CFF_Face cffface = (CFF_Face)face;
+ CFF_Font cff = (CFF_Font)cffface->extra.data;
+ CFF_Charset charset = &cff->charset;
FT_Service_PsCMaps psnames;
FT_String* name;
FT_UShort sid;
FT_UInt i;
- cff = (CFF_FontRec *)face->extra.data;
- charset = &cff->charset;
-
/* CFF2 table does not have glyph names; */
/* we need to use `post' table method */
if ( cff->version_major == 2 )
@@ -408,7 +404,7 @@
if ( service && service->name_index )
- return service->name_index( FT_FACE( face ), glyph_name );
+ return service->name_index( face, glyph_name );
else
{
FT_ERROR(( "cff_get_name_index:"
@@ -446,8 +442,8 @@
FT_DEFINE_SERVICE_GLYPHDICTREC(
cff_service_glyph_dict,
- (FT_GlyphDict_GetNameFunc) cff_get_glyph_name, /* get_name */
- (FT_GlyphDict_NameIndexFunc)cff_get_name_index /* name_index */
+ cff_get_glyph_name, /* FT_GlyphDict_GetNameFunc get_name */
+ cff_get_name_index /* FT_GlyphDict_NameIndexFunc name_index */
)
@@ -456,25 +452,32 @@
*
*/
- static FT_Int
+ FT_CALLBACK_DEF( FT_Int )
cff_ps_has_glyph_names( FT_Face face )
{
return ( face->face_flags & FT_FACE_FLAG_GLYPH_NAMES ) > 0;
}
- static FT_Error
- cff_ps_get_font_info( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_ps_get_font_info( FT_Face face, /* CFF_Face */
PS_FontInfoRec* afont_info )
{
- CFF_Font cff = (CFF_Font)face->extra.data;
- FT_Error error = FT_Err_Ok;
+ CFF_Face cffface = (CFF_Face)face;
+ CFF_Font cff = (CFF_Font)cffface->extra.data;
+ FT_Error error = FT_Err_Ok;
+ if ( cffface->is_cff2 )
+ {
+ error = FT_THROW( Invalid_Argument );
+ goto Fail;
+ }
+
if ( cff && !cff->font_info )
{
CFF_FontRecDict dict = &cff->top_font.font_dict;
- FT_Memory memory = face->root.memory;
+ FT_Memory memory = FT_FACE_MEMORY( face );
PS_FontInfoRec* font_info = NULL;
@@ -507,18 +510,19 @@
}
- static FT_Error
- cff_ps_get_font_extra( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_ps_get_font_extra( FT_Face face, /* CFF_Face */
PS_FontExtraRec* afont_extra )
{
- CFF_Font cff = (CFF_Font)face->extra.data;
- FT_Error error = FT_Err_Ok;
+ CFF_Face cffface = (CFF_Face)face;
+ CFF_Font cff = (CFF_Font)cffface->extra.data;
+ FT_Error error = FT_Err_Ok;
if ( cff && !cff->font_extra )
{
CFF_FontRecDict dict = &cff->top_font.font_dict;
- FT_Memory memory = face->root.memory;
+ FT_Memory memory = FT_FACE_MEMORY( face );
PS_FontExtraRec* font_extra = NULL;
FT_String* embedded_postscript;
@@ -588,13 +592,13 @@
FT_DEFINE_SERVICE_PSINFOREC(
cff_service_ps_info,
- (PS_GetFontInfoFunc) cff_ps_get_font_info, /* ps_get_font_info */
- (PS_GetFontExtraFunc) cff_ps_get_font_extra, /* ps_get_font_extra */
- (PS_HasGlyphNamesFunc) cff_ps_has_glyph_names, /* ps_has_glyph_names */
+ cff_ps_get_font_info, /* PS_GetFontInfoFunc ps_get_font_info */
+ cff_ps_get_font_extra, /* PS_GetFontExtraFunc ps_get_font_extra */
+ cff_ps_has_glyph_names, /* PS_HasGlyphNamesFunc ps_has_glyph_names */
/* unsupported with CFF fonts */
- (PS_GetFontPrivateFunc)NULL, /* ps_get_font_private */
+ NULL, /* PS_GetFontPrivateFunc ps_get_font_private */
/* not implemented */
- (PS_GetFontValueFunc) NULL /* ps_get_font_value */
+ NULL /* PS_GetFontValueFunc ps_get_font_value */
)
@@ -603,17 +607,18 @@
*
*/
- static const char*
- cff_get_ps_name( CFF_Face face )
+ FT_CALLBACK_DEF( const char* )
+ cff_get_ps_name( FT_Face face ) /* CFF_Face */
{
- CFF_Font cff = (CFF_Font)face->extra.data;
- SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+ CFF_Face cffface = (CFF_Face)face;
+ CFF_Font cff = (CFF_Font)cffface->extra.data;
+ SFNT_Service sfnt = (SFNT_Service)cffface->sfnt;
/* following the OpenType specification 1.7, we return the name stored */
/* in the `name' table for a CFF wrapped into an SFNT container */
- if ( FT_IS_SFNT( FT_FACE( face ) ) && sfnt )
+ if ( FT_IS_SFNT( face ) && sfnt )
{
FT_Library library = FT_FACE_LIBRARY( face );
FT_Module sfnt_module = FT_Get_Module( library, "sfnt" );
@@ -625,17 +630,17 @@
if ( service && service->get_ps_font_name )
- return service->get_ps_font_name( FT_FACE( face ) );
+ return service->get_ps_font_name( face );
}
- return (const char*)cff->font_name;
+ return cff ? (const char*)cff->font_name : NULL;
}
FT_DEFINE_SERVICE_PSFONTNAMEREC(
cff_service_ps_name,
- (FT_PsName_GetFunc)cff_get_ps_name /* get_ps_font_name */
+ cff_get_ps_name /* FT_PsName_GetFunc get_ps_font_name */
)
@@ -649,7 +654,7 @@
* Otherwise call the service function in the sfnt module.
*
*/
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
cff_get_cmap_info( FT_CharMap charmap,
TT_CMapInfo *cmap_info )
{
@@ -683,7 +688,7 @@
FT_DEFINE_SERVICE_TTCMAPSREC(
cff_service_get_cmap_info,
- (TT_CMap_Info_GetFunc)cff_get_cmap_info /* get_cmap_info */
+ cff_get_cmap_info /* TT_CMap_Info_GetFunc get_cmap_info */
)
@@ -691,14 +696,15 @@
* CID INFO SERVICE
*
*/
- static FT_Error
- cff_get_ros( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_get_ros( FT_Face face, /* FT_Face */
const char* *registry,
const char* *ordering,
FT_Int *supplement )
{
- FT_Error error = FT_Err_Ok;
- CFF_Font cff = (CFF_Font)face->extra.data;
+ FT_Error error = FT_Err_Ok;
+ CFF_Face cffface = (CFF_Face)face;
+ CFF_Font cff = (CFF_Font)cffface->extra.data;
if ( cff )
@@ -748,12 +754,13 @@
}
- static FT_Error
- cff_get_is_cid( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_get_is_cid( FT_Face face, /* CFF_Face */
FT_Bool *is_cid )
{
- FT_Error error = FT_Err_Ok;
- CFF_Font cff = (CFF_Font)face->extra.data;
+ FT_Error error = FT_Err_Ok;
+ CFF_Face cffface = (CFF_Face)face;
+ CFF_Font cff = (CFF_Font)cffface->extra.data;
*is_cid = 0;
@@ -771,16 +778,15 @@
}
- static FT_Error
- cff_get_cid_from_glyph_index( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_get_cid_from_glyph_index( FT_Face face, /* CFF_Face */
FT_UInt glyph_index,
FT_UInt *cid )
{
- FT_Error error = FT_Err_Ok;
- CFF_Font cff;
-
+ FT_Error error = FT_Err_Ok;
+ CFF_Face cffface = (CFF_Face)face;
+ CFF_Font cff = (CFF_Font)cffface->extra.data;
- cff = (CFF_Font)face->extra.data;
if ( cff )
{
@@ -814,12 +820,12 @@
FT_DEFINE_SERVICE_CIDREC(
cff_service_cid_info,
- (FT_CID_GetRegistryOrderingSupplementFunc)
- cff_get_ros, /* get_ros */
- (FT_CID_GetIsInternallyCIDKeyedFunc)
- cff_get_is_cid, /* get_is_cid */
- (FT_CID_GetCIDFromGlyphIndexFunc)
- cff_get_cid_from_glyph_index /* get_cid_from_glyph_index */
+ cff_get_ros,
+ /* FT_CID_GetRegistryOrderingSupplementFunc get_ros */
+ cff_get_is_cid,
+ /* FT_CID_GetIsInternallyCIDKeyedFunc get_is_cid */
+ cff_get_cid_from_glyph_index
+ /* FT_CID_GetCIDFromGlyphIndexFunc get_cid_from_glyph_index */
)
@@ -831,9 +837,9 @@
FT_DEFINE_SERVICE_PROPERTIESREC(
cff_service_properties,
- (FT_Properties_SetFunc)ps_property_set, /* set_property */
- (FT_Properties_GetFunc)ps_property_get ) /* get_property */
-
+ ps_property_set, /* FT_Properties_SetFunc set_property */
+ ps_property_get /* FT_Properties_GetFunc get_property */
+ )
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
@@ -842,160 +848,195 @@
*
*/
- static FT_Error
- cff_set_mm_blend( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_set_mm_blend( FT_Face face, /* CFF_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->set_mm_blend( FT_FACE( face ), num_coords, coords );
+ return mm->set_mm_blend( face, num_coords, coords );
}
- static FT_Error
- cff_get_mm_blend( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_get_mm_blend( FT_Face face, /* CFF_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->get_mm_blend( FT_FACE( face ), num_coords, coords );
+ return mm->get_mm_blend( face, num_coords, coords );
}
- static FT_Error
- cff_set_mm_weightvector( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_set_mm_weightvector( FT_Face face, /* CFF_Face */
FT_UInt len,
FT_Fixed* weightvector )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->set_mm_weightvector( FT_FACE( face ), len, weightvector );
+ return mm->set_mm_weightvector( face, len, weightvector );
}
- static FT_Error
- cff_get_mm_weightvector( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_get_mm_weightvector( FT_Face face, /* CFF_Face */
FT_UInt* len,
FT_Fixed* weightvector )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->get_mm_weightvector( FT_FACE( face ), len, weightvector );
+ return mm->get_mm_weightvector( face, len, weightvector );
}
- static FT_Error
- cff_get_mm_var( CFF_Face face,
+ FT_CALLBACK_DEF( void )
+ cff_construct_ps_name( FT_Face face ) /* CFF_Face */
+ {
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
+
+
+ mm->construct_ps_name( face );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ cff_get_mm_var( FT_Face face, /* CFF_Face */
FT_MM_Var* *master )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->get_mm_var( FT_FACE( face ), master );
+ return mm->get_mm_var( face, master );
}
- static FT_Error
- cff_set_var_design( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_set_var_design( FT_Face face, /* CFF_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->set_var_design( FT_FACE( face ), num_coords, coords );
+ return mm->set_var_design( face, num_coords, coords );
}
- static FT_Error
- cff_get_var_design( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_get_var_design( FT_Face face, /* CFF_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->get_var_design( FT_FACE( face ), num_coords, coords );
+ return mm->get_var_design( face, num_coords, coords );
}
- static FT_Error
- cff_set_instance( CFF_Face face,
- FT_UInt instance_index )
+ FT_CALLBACK_DEF( FT_Error )
+ cff_set_named_instance( FT_Face face, /* CFF_Face */
+ FT_UInt instance_index )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->set_instance( FT_FACE( face ), instance_index );
+ return mm->set_named_instance( face, instance_index );
}
- static FT_Error
- cff_load_item_variation_store( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_get_default_named_instance( FT_Face face, /* CFF_Face */
+ FT_UInt *instance_index )
+ {
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
+
+
+ return mm->get_default_named_instance( face, instance_index );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ cff_load_item_variation_store( FT_Face face, /* CFF_Face */
FT_ULong offset,
GX_ItemVarStore itemStore )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->load_item_var_store( FT_FACE(face), offset, itemStore );
+ return mm->load_item_var_store( face, offset, itemStore );
}
- static FT_Error
- cff_load_delta_set_index_mapping( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_load_delta_set_index_mapping( FT_Face face, /* CFF_Face */
FT_ULong offset,
GX_DeltaSetIdxMap map,
GX_ItemVarStore itemStore,
FT_ULong table_len )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->load_delta_set_idx_map( FT_FACE( face ), offset, map,
+ return mm->load_delta_set_idx_map( face, offset, map,
itemStore, table_len );
}
- static FT_Int
- cff_get_item_delta( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Int )
+ cff_get_item_delta( FT_Face face, /* CFF_Face */
GX_ItemVarStore itemStore,
FT_UInt outerIndex,
FT_UInt innerIndex )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->get_item_delta( FT_FACE( face ), itemStore,
- outerIndex, innerIndex );
+ return mm->get_item_delta( face, itemStore, outerIndex, innerIndex );
}
- static void
- cff_done_item_variation_store( CFF_Face face,
+ FT_CALLBACK_DEF( void )
+ cff_done_item_variation_store( FT_Face face, /* CFF_Face */
GX_ItemVarStore itemStore )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- mm->done_item_var_store( FT_FACE( face ), itemStore );
+ mm->done_item_var_store( face, itemStore );
}
- static void
- cff_done_delta_set_index_map( CFF_Face face,
+ FT_CALLBACK_DEF( void )
+ cff_done_delta_set_index_map( FT_Face face, /* CFF_Face */
GX_DeltaSetIdxMap deltaSetIdxMap )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- mm->done_delta_set_idx_map( FT_FACE ( face ), deltaSetIdxMap );
+ mm->done_delta_set_idx_map( face, deltaSetIdxMap );
}
@@ -1003,36 +1044,35 @@
FT_DEFINE_SERVICE_MULTIMASTERSREC(
cff_service_multi_masters,
- (FT_Get_MM_Func) NULL, /* get_mm */
- (FT_Set_MM_Design_Func) NULL, /* set_mm_design */
- (FT_Set_MM_Blend_Func) cff_set_mm_blend, /* set_mm_blend */
- (FT_Get_MM_Blend_Func) cff_get_mm_blend, /* get_mm_blend */
- (FT_Get_MM_Var_Func) cff_get_mm_var, /* get_mm_var */
- (FT_Set_Var_Design_Func)cff_set_var_design, /* set_var_design */
- (FT_Get_Var_Design_Func)cff_get_var_design, /* get_var_design */
- (FT_Set_Instance_Func) cff_set_instance, /* set_instance */
- (FT_Set_MM_WeightVector_Func)
- cff_set_mm_weightvector,
- /* set_mm_weightvector */
- (FT_Get_MM_WeightVector_Func)
- cff_get_mm_weightvector,
- /* get_mm_weightvector */
- (FT_Var_Load_Delta_Set_Idx_Map_Func)
- cff_load_delta_set_index_mapping,
- /* load_delta_set_idx_map */
- (FT_Var_Load_Item_Var_Store_Func)
- cff_load_item_variation_store,
- /* load_item_variation_store */
- (FT_Var_Get_Item_Delta_Func)
- cff_get_item_delta, /* get_item_delta */
- (FT_Var_Done_Item_Var_Store_Func)
- cff_done_item_variation_store,
- /* done_item_variation_store */
- (FT_Var_Done_Delta_Set_Idx_Map_Func)
- cff_done_delta_set_index_map,
- /* done_delta_set_index_map */
- (FT_Get_Var_Blend_Func) cff_get_var_blend, /* get_var_blend */
- (FT_Done_Blend_Func) cff_done_blend /* done_blend */
+ NULL, /* FT_Get_MM_Func get_mm */
+ NULL, /* FT_Set_MM_Design_Func set_mm_design */
+ cff_set_mm_blend, /* FT_Set_MM_Blend_Func set_mm_blend */
+ cff_get_mm_blend, /* FT_Get_MM_Blend_Func get_mm_blend */
+ cff_get_mm_var, /* FT_Get_MM_Var_Func get_mm_var */
+ cff_set_var_design, /* FT_Set_Var_Design_Func set_var_design */
+ cff_get_var_design, /* FT_Get_Var_Design_Func get_var_design */
+ cff_set_named_instance,
+ /* FT_Set_Named_Instance_Func set_named_instance */
+ cff_get_default_named_instance,
+ /* FT_Get_Default_Named_Instance_Func get_default_named_instance */
+ cff_set_mm_weightvector,
+ /* FT_Set_MM_WeightVector_Func set_mm_weightvector */
+ cff_get_mm_weightvector,
+ /* FT_Get_MM_WeightVector_Func get_mm_weightvector */
+ cff_construct_ps_name,
+ /* FT_Construct_PS_Name_Func construct_ps_name */
+ cff_load_delta_set_index_mapping,
+ /* FT_Var_Load_Delta_Set_Idx_Map_Func load_delta_set_idx_map */
+ cff_load_item_variation_store,
+ /* FT_Var_Load_Item_Var_Store_Func load_item_variation_store */
+ cff_get_item_delta,
+ /* FT_Var_Get_Item_Delta_Func get_item_delta */
+ cff_done_item_variation_store,
+ /* FT_Var_Done_Item_Var_Store_Func done_item_variation_store */
+ cff_done_delta_set_index_map,
+ /* FT_Var_Done_Delta_Set_Idx_Map_Func done_delta_set_index_map */
+ cff_get_var_blend, /* FT_Get_Var_Blend_Func get_var_blend */
+ cff_done_blend /* FT_Done_Blend_Func done_blend */
)
@@ -1041,41 +1081,46 @@
*
*/
- static FT_Error
- cff_hadvance_adjust( CFF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cff_hadvance_adjust( FT_Face face, /* CFF_Face */
FT_UInt gindex,
FT_Int *avalue )
{
- FT_Service_MetricsVariations var = (FT_Service_MetricsVariations)face->var;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MetricsVariations
+ var = (FT_Service_MetricsVariations)cffface->tt_var;
- return var->hadvance_adjust( FT_FACE( face ), gindex, avalue );
+ return var->hadvance_adjust( face, gindex, avalue );
}
- static void
- cff_metrics_adjust( CFF_Face face )
+ FT_CALLBACK_DEF( void )
+ cff_metrics_adjust( FT_Face face ) /* CFF_Face */
{
- FT_Service_MetricsVariations var = (FT_Service_MetricsVariations)face->var;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MetricsVariations
+ var = (FT_Service_MetricsVariations)cffface->tt_var;
- var->metrics_adjust( FT_FACE( face ) );
+ var->metrics_adjust( face );
}
FT_DEFINE_SERVICE_METRICSVARIATIONSREC(
cff_service_metrics_variations,
- (FT_HAdvance_Adjust_Func)cff_hadvance_adjust, /* hadvance_adjust */
- (FT_LSB_Adjust_Func) NULL, /* lsb_adjust */
- (FT_RSB_Adjust_Func) NULL, /* rsb_adjust */
+ cff_hadvance_adjust, /* FT_HAdvance_Adjust_Func hadvance_adjust */
+ NULL, /* FT_LSB_Adjust_Func lsb_adjust */
+ NULL, /* FT_RSB_Adjust_Func rsb_adjust */
- (FT_VAdvance_Adjust_Func)NULL, /* vadvance_adjust */
- (FT_TSB_Adjust_Func) NULL, /* tsb_adjust */
- (FT_BSB_Adjust_Func) NULL, /* bsb_adjust */
- (FT_VOrg_Adjust_Func) NULL, /* vorg_adjust */
+ NULL, /* FT_VAdvance_Adjust_Func vadvance_adjust */
+ NULL, /* FT_TSB_Adjust_Func tsb_adjust */
+ NULL, /* FT_BSB_Adjust_Func bsb_adjust */
+ NULL, /* FT_VOrg_Adjust_Func vorg_adjust */
- (FT_Metrics_Adjust_Func) cff_metrics_adjust /* metrics_adjust */
+ cff_metrics_adjust, /* FT_Metrics_Adjust_Func metrics_adjust */
+ NULL /* FT_Size_Reset_Func size_reset */
)
#endif
@@ -1088,11 +1133,11 @@
FT_DEFINE_SERVICE_CFFLOADREC(
cff_service_cff_load,
- (FT_Get_Standard_Encoding_Func)cff_get_standard_encoding,
- (FT_Load_Private_Dict_Func) cff_load_private_dict,
- (FT_FD_Select_Get_Func) cff_fd_select_get,
- (FT_Blend_Check_Vector_Func) cff_blend_check_vector,
- (FT_Blend_Build_Vector_Func) cff_blend_build_vector
+ cff_get_standard_encoding, /* FT_Get_Standard_Encoding_Func get_standard_encoding */
+ cff_load_private_dict, /* FT_Load_Private_Dict_Func load_private_dict */
+ cff_fd_select_get, /* FT_FD_Select_Get_Func fd_select_get */
+ cff_blend_check_vector, /* FT_Blend_Check_Vector_Func blend_check_vector */
+ cff_blend_build_vector /* FT_Blend_Build_Vector_Func blend_build_vector */
)
diff --git a/thirdparty/freetype/src/cff/cffgload.c b/thirdparty/freetype/src/cff/cffgload.c
index cfa0aaf2b6..c483d1d1a5 100644
--- a/thirdparty/freetype/src/cff/cffgload.c
+++ b/thirdparty/freetype/src/cff/cffgload.c
@@ -356,14 +356,16 @@
#ifdef FT_CONFIG_OPTION_SVG
/* check for OT-SVG */
- if ( ( load_flags & FT_LOAD_COLOR ) && face->svg )
+ if ( ( load_flags & FT_LOAD_NO_SVG ) == 0 &&
+ ( load_flags & FT_LOAD_COLOR ) &&
+ face->svg )
{
/*
* We load the SVG document and try to grab the advances from the
* table. For the bearings we rely on the presetting hook to do that.
*/
- SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
if ( size && (size->root.metrics.x_ppem < 1 ||
diff --git a/thirdparty/freetype/src/cff/cffload.c b/thirdparty/freetype/src/cff/cffload.c
index 4b8c6e16c5..f96002ec0b 100644
--- a/thirdparty/freetype/src/cff/cffload.c
+++ b/thirdparty/freetype/src/cff/cffload.c
@@ -400,7 +400,7 @@
/* Allocate a table containing pointers to an index's elements. */
/* The `pool' argument makes this function convert the index */
- /* entries to C-style strings (this is, null-terminated). */
+ /* entries to C-style strings (that is, null-terminated). */
static FT_Error
cff_index_get_pointers( CFF_Index idx,
FT_Byte*** table,
@@ -1589,16 +1589,17 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
FT_LOCAL_DEF( FT_Error )
- cff_get_var_blend( CFF_Face face,
+ cff_get_var_blend( FT_Face face, /* CFF_Face */
FT_UInt *num_coords,
FT_Fixed* *coords,
FT_Fixed* *normalizedcoords,
FT_MM_Var* *mm_var )
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- return mm->get_var_blend( FT_FACE( face ),
+ return mm->get_var_blend( face,
num_coords,
coords,
normalizedcoords,
@@ -1607,13 +1608,14 @@
FT_LOCAL_DEF( void )
- cff_done_blend( CFF_Face face )
+ cff_done_blend( FT_Face face ) /* CFF_Face */
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
+ CFF_Face cffface = (CFF_Face)face;
+ FT_Service_MultiMasters mm = (FT_Service_MultiMasters)cffface->mm;
- if (mm)
- mm->done_blend( FT_FACE( face ) );
+ if ( mm )
+ mm->done_blend( face );
}
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
@@ -1650,13 +1652,6 @@
goto Exit;
}
- /* Zero out the code to gid/sid mappings. */
- for ( j = 0; j < 256; j++ )
- {
- encoding->sids [j] = 0;
- encoding->codes[j] = 0;
- }
-
/* Note: The encoding table in a CFF font is indexed by glyph index; */
/* the first encoded glyph index is 1. Hence, we read the character */
/* code (`glyph_code') at index j and make the assignment: */
@@ -1671,6 +1666,10 @@
if ( offset > 1 )
{
+ /* Zero out the code to gid/sid mappings. */
+ FT_ARRAY_ZERO( encoding->sids, 256 );
+ FT_ARRAY_ZERO( encoding->codes, 256 );
+
encoding->offset = base_offset + offset;
/* we need to parse the table to determine its size */
@@ -2012,7 +2011,7 @@
/* Top and Font DICTs are not allowed to have blend operators. */
error = cff_parser_init( &parser,
code,
- &subfont->font_dict,
+ top,
font->library,
stackSize,
0,
diff --git a/thirdparty/freetype/src/cff/cffload.h b/thirdparty/freetype/src/cff/cffload.h
index 5a41cdebc8..b5286b0c8c 100644
--- a/thirdparty/freetype/src/cff/cffload.h
+++ b/thirdparty/freetype/src/cff/cffload.h
@@ -105,14 +105,14 @@ FT_BEGIN_HEADER
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
FT_LOCAL( FT_Error )
- cff_get_var_blend( CFF_Face face,
+ cff_get_var_blend( FT_Face face,
FT_UInt *num_coords,
FT_Fixed* *coords,
FT_Fixed* *normalizedcoords,
FT_MM_Var* *mm_var );
FT_LOCAL( void )
- cff_done_blend( CFF_Face face );
+ cff_done_blend( FT_Face face );
#endif
diff --git a/thirdparty/freetype/src/cff/cffobjs.c b/thirdparty/freetype/src/cff/cffobjs.c
index 40cd9bf917..6d08620c48 100644
--- a/thirdparty/freetype/src/cff/cffobjs.c
+++ b/thirdparty/freetype/src/cff/cffobjs.c
@@ -69,8 +69,8 @@
FT_Module module;
- module = FT_Get_Module( size->root.face->driver->root.library,
- "pshinter" );
+ module = FT_Get_Module( font->library, "pshinter" );
+
return ( module && pshinter && pshinter->get_globals_funcs )
? pshinter->get_globals_funcs( module )
: 0;
@@ -182,8 +182,7 @@
goto Exit;
cff_make_private_dict( &font->top_font, &priv );
- error = funcs->create( cffsize->face->memory, &priv,
- &internal->topfont );
+ error = funcs->create( memory, &priv, &internal->topfont );
if ( error )
goto Exit;
@@ -193,8 +192,7 @@
cff_make_private_dict( sub, &priv );
- error = funcs->create( cffsize->face->memory, &priv,
- &internal->subfonts[i - 1] );
+ error = funcs->create( memory, &priv, &internal->subfonts[i - 1] );
if ( error )
goto Exit;
}
@@ -381,8 +379,7 @@
FT_Module module;
- module = FT_Get_Module( slot->face->driver->root.library,
- "pshinter" );
+ module = FT_Get_Module( slot->library, "pshinter" );
if ( module )
{
T2_Hints_Funcs funcs;
@@ -722,22 +719,15 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
{
- FT_Service_MultiMasters mm = (FT_Service_MultiMasters)face->mm;
- FT_Service_MetricsVariations var = (FT_Service_MetricsVariations)face->var;
-
FT_UInt instance_index = (FT_UInt)face_index >> 16;
if ( FT_HAS_MULTIPLE_MASTERS( cffface ) &&
- mm &&
instance_index > 0 )
{
- error = mm->set_instance( cffface, instance_index );
+ error = FT_Set_Named_Instance( cffface, instance_index );
if ( error )
goto Exit;
-
- if ( var )
- var->metrics_adjust( cffface );
}
}
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
@@ -1160,7 +1150,7 @@
}
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
- cff_done_blend( face );
+ cff_done_blend( cffface );
face->blend = NULL;
#endif
}
diff --git a/thirdparty/freetype/src/cff/cffparse.c b/thirdparty/freetype/src/cff/cffparse.c
index e16206fd55..c850dfc61e 100644
--- a/thirdparty/freetype/src/cff/cffparse.c
+++ b/thirdparty/freetype/src/cff/cffparse.c
@@ -63,10 +63,7 @@
/* allocate the stack buffer */
if ( FT_QNEW_ARRAY( parser->stack, stackSize ) )
- {
- FT_FREE( parser->stack );
goto Exit;
- }
parser->stackSize = stackSize;
parser->top = parser->stack; /* empty stack */
@@ -76,23 +73,6 @@
}
-#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
- static void
- finalize_t2_strings( FT_Memory memory,
- void* data,
- void* user )
- {
- CFF_T2_String t2 = (CFF_T2_String)data;
-
-
- FT_UNUSED( user );
-
- memory->free( memory, t2->start );
- memory->free( memory, data );
- }
-#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */
-
-
FT_LOCAL_DEF( void )
cff_parser_done( CFF_Parser parser )
{
@@ -102,63 +82,19 @@
FT_FREE( parser->stack );
#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
- FT_List_Finalize( &parser->t2_strings,
- finalize_t2_strings,
- memory,
- NULL );
+ FT_List_Finalize( &parser->t2_strings, NULL, memory, NULL );
#endif
}
- /* Assuming `first >= last'. */
-
- static FT_Error
- cff_parser_within_limits( CFF_Parser parser,
- FT_Byte* first,
- FT_Byte* last )
- {
-#ifndef CFF_CONFIG_OPTION_OLD_ENGINE
-
- /* Fast path for regular FreeType builds with the "new" engine; */
- /* `first >= parser->start' can be assumed. */
-
- FT_UNUSED( first );
-
- return last < parser->limit ? FT_Err_Ok : FT_THROW( Invalid_Argument );
-
-#else /* CFF_CONFIG_OPTION_OLD_ENGINE */
-
- FT_ListNode node;
-
-
- if ( first >= parser->start &&
- last < parser->limit )
- return FT_Err_Ok;
-
- node = parser->t2_strings.head;
-
- while ( node )
- {
- CFF_T2_String t2 = (CFF_T2_String)node->data;
-
-
- if ( first >= t2->start &&
- last < t2->limit )
- return FT_Err_Ok;
-
- node = node->next;
- }
-
- return FT_THROW( Invalid_Argument );
-
-#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */
- }
-
+ /* The parser limit checks in the next two functions are supposed */
+ /* to detect the immediate crossing of the stream boundary. They */
+ /* shall not be triggered from the distant t2_strings buffers. */
/* read an integer */
static FT_Long
- cff_parse_integer( CFF_Parser parser,
- FT_Byte* start )
+ cff_parse_integer( FT_Byte* start,
+ FT_Byte* limit )
{
FT_Byte* p = start;
FT_Int v = *p++;
@@ -167,14 +103,14 @@
if ( v == 28 )
{
- if ( cff_parser_within_limits( parser, p, p + 1 ) )
+ if ( p + 2 > limit && limit >= p )
goto Bad;
val = (FT_Short)( ( (FT_UShort)p[0] << 8 ) | p[1] );
}
else if ( v == 29 )
{
- if ( cff_parser_within_limits( parser, p, p + 3 ) )
+ if ( p + 4 > limit && limit >= p )
goto Bad;
val = (FT_Long)( ( (FT_ULong)p[0] << 24 ) |
@@ -188,14 +124,14 @@
}
else if ( v < 251 )
{
- if ( cff_parser_within_limits( parser, p, p ) )
+ if ( p + 1 > limit && limit >= p )
goto Bad;
val = ( v - 247 ) * 256 + p[0] + 108;
}
else
{
- if ( cff_parser_within_limits( parser, p, p ) )
+ if ( p + 1 > limit && limit >= p )
goto Bad;
val = -( v - 251 ) * 256 - p[0] - 108;
@@ -244,10 +180,10 @@
/* read a real */
static FT_Fixed
- cff_parse_real( CFF_Parser parser,
- FT_Byte* start,
- FT_Long power_ten,
- FT_Long* scaling )
+ cff_parse_real( FT_Byte* start,
+ FT_Byte* limit,
+ FT_Long power_ten,
+ FT_Long* scaling )
{
FT_Byte* p = start;
FT_Int nib;
@@ -282,7 +218,7 @@
p++;
/* Make sure we don't read past the end. */
- if ( cff_parser_within_limits( parser, p, p ) )
+ if ( p + 1 > limit && limit >= p )
goto Bad;
}
@@ -319,7 +255,7 @@
p++;
/* Make sure we don't read past the end. */
- if ( cff_parser_within_limits( parser, p, p ) )
+ if ( p + 1 > limit && limit >= p )
goto Bad;
}
@@ -358,7 +294,7 @@
p++;
/* Make sure we don't read past the end. */
- if ( cff_parser_within_limits( parser, p, p ) )
+ if ( p + 1 > limit && limit >= p )
goto Bad;
}
@@ -525,7 +461,7 @@
if ( **d == 30 )
{
/* binary-coded decimal is truncated to integer */
- return cff_parse_real( parser, *d, 0, NULL ) >> 16;
+ return cff_parse_real( *d, parser->limit, 0, NULL ) >> 16;
}
else if ( **d == 255 )
@@ -551,7 +487,7 @@
}
else
- return cff_parse_integer( parser, *d );
+ return cff_parse_integer( *d, parser->limit );
}
@@ -562,10 +498,10 @@
FT_Long scaling )
{
if ( **d == 30 )
- return cff_parse_real( parser, *d, scaling, NULL );
+ return cff_parse_real( *d, parser->limit, scaling, NULL );
else
{
- FT_Long val = cff_parse_integer( parser, *d );
+ FT_Long val = cff_parse_integer( *d, parser->limit );
if ( scaling )
@@ -630,14 +566,14 @@
FT_ASSERT( scaling );
if ( **d == 30 )
- return cff_parse_real( parser, *d, 0, scaling );
+ return cff_parse_real( *d, parser->limit, 0, scaling );
else
{
FT_Long number;
FT_Int integer_length;
- number = cff_parse_integer( parser, d[0] );
+ number = cff_parse_integer( *d, parser->limit );
if ( number > 0x7FFFL )
{
@@ -686,7 +622,7 @@
dict->has_font_matrix = TRUE;
- /* We expect a well-formed font matrix, this is, the matrix elements */
+ /* We expect a well-formed font matrix, that is, the matrix elements */
/* `xx' and `yy' are of approximately the same magnitude. To avoid */
/* loss of precision, we use the magnitude of the largest matrix */
/* element to scale all other elements. The scaling factor is then */
@@ -1264,11 +1200,8 @@
FT_Byte* charstring_base;
FT_ULong charstring_len;
- FT_Fixed* stack;
- FT_ListNode node;
- CFF_T2_String t2;
- FT_Fixed t2_size;
- FT_Byte* q;
+ FT_Fixed* stack;
+ FT_Byte* q = NULL;
charstring_base = ++p;
@@ -1309,39 +1242,18 @@
/* Now copy the stack data in the temporary decoder object, */
/* converting it back to charstring number representations */
/* (this is ugly, I know). */
+ /* The maximum required size is 5 bytes per stack element. */
+ if ( FT_QALLOC( q, (FT_Long)( 2 * sizeof ( FT_ListNode ) ) +
+ 5 * ( decoder.top - decoder.stack ) ) )
+ goto Exit;
- node = (FT_ListNode)memory->alloc( memory,
- sizeof ( FT_ListNodeRec ) );
- if ( !node )
- goto Out_Of_Memory_Error;
-
- FT_List_Add( &parser->t2_strings, node );
-
- t2 = (CFF_T2_String)memory->alloc( memory,
- sizeof ( CFF_T2_StringRec ) );
- if ( !t2 )
- goto Out_Of_Memory_Error;
-
- node->data = t2;
-
- /* `5' is the conservative upper bound of required bytes per stack */
- /* element. */
-
- t2_size = 5 * ( decoder.top - decoder.stack );
-
- q = (FT_Byte*)memory->alloc( memory, t2_size );
- if ( !q )
- goto Out_Of_Memory_Error;
-
- t2->start = q;
- t2->limit = q + t2_size;
+ FT_List_Add( &parser->t2_strings, (FT_ListNode)q );
- stack = decoder.stack;
+ q += 2 * sizeof ( FT_ListNode );
- while ( stack < decoder.top )
+ for ( stack = decoder.stack; stack < decoder.top; stack++ )
{
- FT_ULong num;
- FT_Bool neg;
+ FT_Long num = *stack;
if ( (FT_UInt)( parser->top - parser->stack ) >= parser->stackSize )
@@ -1349,69 +1261,37 @@
*parser->top++ = q;
- if ( *stack < 0 )
- {
- num = (FT_ULong)NEG_LONG( *stack );
- neg = 1;
- }
- else
- {
- num = (FT_ULong)*stack;
- neg = 0;
- }
-
if ( num & 0xFFFFU )
{
- if ( neg )
- num = (FT_ULong)-num;
-
*q++ = 255;
- *q++ = ( num & 0xFF000000U ) >> 24;
- *q++ = ( num & 0x00FF0000U ) >> 16;
- *q++ = ( num & 0x0000FF00U ) >> 8;
- *q++ = num & 0x000000FFU;
+ *q++ = (FT_Byte)( ( num >> 24 ) & 0xFF );
+ *q++ = (FT_Byte)( ( num >> 16 ) & 0xFF );
+ *q++ = (FT_Byte)( ( num >> 8 ) & 0xFF );
+ *q++ = (FT_Byte)( ( num ) & 0xFF );
}
else
{
num >>= 16;
- if ( neg )
+ if ( -107 <= num && num <= 107 )
+ *q++ = (FT_Byte)( num + 139 );
+ else if ( 108 <= num && num <= 1131 )
{
- if ( num <= 107 )
- *q++ = (FT_Byte)( 139 - num );
- else if ( num <= 1131 )
- {
- *q++ = (FT_Byte)( ( ( num - 108 ) >> 8 ) + 251 );
- *q++ = (FT_Byte)( ( num - 108 ) & 0xFF );
- }
- else
- {
- num = (FT_ULong)-num;
-
- *q++ = 28;
- *q++ = (FT_Byte)( num >> 8 );
- *q++ = (FT_Byte)( num & 0xFF );
- }
+ *q++ = (FT_Byte)( ( ( num - 108 ) >> 8 ) + 247 );
+ *q++ = (FT_Byte)( ( num - 108 ) & 0xFF );
+ }
+ else if ( -1131 <= num && num <= -108 )
+ {
+ *q++ = (FT_Byte)( ( ( -num - 108 ) >> 8 ) + 251 );
+ *q++ = (FT_Byte)( ( -num - 108) & 0xFF );
}
else
{
- if ( num <= 107 )
- *q++ = (FT_Byte)( num + 139 );
- else if ( num <= 1131 )
- {
- *q++ = (FT_Byte)( ( ( num - 108 ) >> 8 ) + 247 );
- *q++ = (FT_Byte)( ( num - 108 ) & 0xFF );
- }
- else
- {
- *q++ = 28;
- *q++ = (FT_Byte)( num >> 8 );
- *q++ = (FT_Byte)( num & 0xFF );
- }
+ *q++ = 28;
+ *q++ = (FT_Byte)( num >> 8 );
+ *q++ = (FT_Byte)( num & 0xFF );
}
}
-
- stack++;
}
}
#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */
@@ -1598,12 +1478,6 @@
Exit:
return error;
-#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
- Out_Of_Memory_Error:
- error = FT_THROW( Out_Of_Memory );
- goto Exit;
-#endif
-
Stack_Overflow:
error = FT_THROW( Invalid_Argument );
goto Exit;
diff --git a/thirdparty/freetype/src/cff/cffparse.h b/thirdparty/freetype/src/cff/cffparse.h
index 58d59fa4ac..b6378a8e8d 100644
--- a/thirdparty/freetype/src/cff/cffparse.h
+++ b/thirdparty/freetype/src/cff/cffparse.h
@@ -133,15 +133,6 @@ FT_BEGIN_HEADER
FT_END_HEADER
-#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
- typedef struct CFF_T2_String_
- {
- FT_Byte* start;
- FT_Byte* limit;
-
- } CFF_T2_StringRec, *CFF_T2_String;
-#endif /* CFF_CONFIG_OPTION_OLD_ENGINE */
-
#endif /* CFFPARSE_H_ */
diff --git a/thirdparty/freetype/src/cid/cidgload.c b/thirdparty/freetype/src/cid/cidgload.c
index ba4b7565d5..eaca765ad0 100644
--- a/thirdparty/freetype/src/cid/cidgload.c
+++ b/thirdparty/freetype/src/cid/cidgload.c
@@ -40,6 +40,117 @@
#define FT_COMPONENT cidgload
+ /*
+ * A helper function to compute FD number (`fd_select`), the offset to the
+ * head of the glyph data (`off1`), and the offset to the and of the glyph
+ * data (`off2`).
+ *
+ * The number how many times `cid_get_offset` is invoked can be controlled
+ * by the number of non-NULL arguments. If `fd_select` is non-NULL but
+ * `off1` and `off2` are NULL, `cid_get_offset` is invoked only for
+ * `fd_select`; `off1` and `off2` are not validated.
+ *
+ */
+ FT_LOCAL_DEF( FT_Error )
+ cid_compute_fd_and_offsets( CID_Face face,
+ FT_UInt glyph_index,
+ FT_ULong* fd_select_p,
+ FT_ULong* off1_p,
+ FT_ULong* off2_p )
+ {
+ FT_Error error = FT_Err_Ok;
+
+ CID_FaceInfo cid = &face->cid;
+ FT_Stream stream = face->cid_stream;
+ FT_UInt entry_len = cid->fd_bytes + cid->gd_bytes;
+
+ FT_Byte* p;
+ FT_Bool need_frame_exit = 0;
+ FT_ULong fd_select, off1, off2;
+
+
+ /* For ordinary fonts, read the CID font dictionary index */
+ /* and charstring offset from the CIDMap. */
+
+ if ( FT_STREAM_SEEK( cid->data_offset + cid->cidmap_offset +
+ glyph_index * entry_len ) ||
+ FT_FRAME_ENTER( 2 * entry_len ) )
+ goto Exit;
+
+ need_frame_exit = 1;
+
+ p = (FT_Byte*)stream->cursor;
+ fd_select = cid_get_offset( &p, cid->fd_bytes );
+ off1 = cid_get_offset( &p, cid->gd_bytes );
+
+ p += cid->fd_bytes;
+ off2 = cid_get_offset( &p, cid->gd_bytes );
+
+ if ( fd_select_p )
+ *fd_select_p = fd_select;
+ if ( off1_p )
+ *off1_p = off1;
+ if ( off2_p )
+ *off2_p = off2;
+
+ if ( fd_select >= cid->num_dicts )
+ {
+ /*
+ * fd_select == 0xFF is often used to indicate that the CID
+ * has no charstring to be rendered, similar to GID = 0xFFFF
+ * in TrueType fonts.
+ */
+ if ( ( cid->fd_bytes == 1 && fd_select == 0xFFU ) ||
+ ( cid->fd_bytes == 2 && fd_select == 0xFFFFU ) )
+ {
+ FT_TRACE1(( "cid_load_glyph: fail for glyph index %d:\n",
+ glyph_index ));
+ FT_TRACE1(( " FD number %ld is the maximum\n",
+ fd_select ));
+ FT_TRACE1(( " integer fitting into %d byte%s\n",
+ cid->fd_bytes, cid->fd_bytes == 1 ? "" : "s" ));
+ }
+ else
+ {
+ FT_TRACE0(( "cid_load_glyph: fail for glyph index %d:\n",
+ glyph_index ));
+ FT_TRACE0(( " FD number %ld is larger\n",
+ fd_select ));
+ FT_TRACE0(( " than number of dictionaries (%d)\n",
+ cid->num_dicts ));
+ }
+
+ error = FT_THROW( Invalid_Offset );
+ goto Exit;
+ }
+ else if ( off2 > stream->size )
+ {
+ FT_TRACE0(( "cid_load_glyph: fail for glyph index %d:\n",
+ glyph_index ));
+ FT_TRACE0(( " end of the glyph data\n" ));
+ FT_TRACE0(( " is beyond the data stream\n" ));
+
+ error = FT_THROW( Invalid_Offset );
+ goto Exit;
+ }
+ else if ( off1 > off2 )
+ {
+ FT_TRACE0(( "cid_load_glyph: fail for glyph index %d:\n",
+ glyph_index ));
+ FT_TRACE0(( " the end position of glyph data\n" ));
+ FT_TRACE0(( " is set before the start position\n" ));
+
+ error = FT_THROW( Invalid_Offset );
+ }
+
+ Exit:
+ if ( need_frame_exit )
+ FT_FRAME_EXIT();
+
+ return error;
+ }
+
+
FT_CALLBACK_DEF( FT_Error )
cid_load_glyph( T1_Decoder decoder,
FT_UInt glyph_index )
@@ -97,34 +208,14 @@
else
#endif /* FT_CONFIG_OPTION_INCREMENTAL */
-
- /* For ordinary fonts read the CID font dictionary index */
- /* and charstring offset from the CIDMap. */
{
- FT_UInt entry_len = cid->fd_bytes + cid->gd_bytes;
FT_ULong off1, off2;
- if ( FT_STREAM_SEEK( cid->data_offset + cid->cidmap_offset +
- glyph_index * entry_len ) ||
- FT_FRAME_ENTER( 2 * entry_len ) )
- goto Exit;
-
- p = (FT_Byte*)stream->cursor;
- fd_select = cid_get_offset( &p, cid->fd_bytes );
- off1 = cid_get_offset( &p, cid->gd_bytes );
- p += cid->fd_bytes;
- off2 = cid_get_offset( &p, cid->gd_bytes );
- FT_FRAME_EXIT();
-
- if ( fd_select >= cid->num_dicts ||
- off2 > stream->size ||
- off1 > off2 )
- {
- FT_TRACE0(( "cid_load_glyph: invalid glyph stream offsets\n" ));
- error = FT_THROW( Invalid_Offset );
+ error = cid_compute_fd_and_offsets( face, glyph_index,
+ &fd_select, &off1, &off2 );
+ if ( error )
goto Exit;
- }
glyph_length = off2 - off1;
@@ -161,7 +252,9 @@
cs_offset = decoder->lenIV >= 0 ? (FT_UInt)decoder->lenIV : 0;
if ( cs_offset > glyph_length )
{
- FT_TRACE0(( "cid_load_glyph: invalid glyph stream offsets\n" ));
+ FT_TRACE0(( "cid_load_glyph: fail for glyph_index=%d, "
+ "offset to the charstring is beyond glyph length\n",
+ glyph_index ));
error = FT_THROW( Invalid_Offset );
goto Exit;
}
diff --git a/thirdparty/freetype/src/cid/cidgload.h b/thirdparty/freetype/src/cid/cidgload.h
index 97954d418f..edd6229234 100644
--- a/thirdparty/freetype/src/cid/cidgload.h
+++ b/thirdparty/freetype/src/cid/cidgload.h
@@ -42,6 +42,14 @@ FT_BEGIN_HEADER
FT_Int32 load_flags );
+ FT_LOCAL( FT_Error )
+ cid_compute_fd_and_offsets( CID_Face face,
+ FT_UInt glyph_index,
+ FT_ULong* fd_select_p,
+ FT_ULong* off1_p,
+ FT_ULong* off2_p );
+
+
FT_END_HEADER
#endif /* CIDGLOAD_H_ */
diff --git a/thirdparty/freetype/src/cid/cidload.c b/thirdparty/freetype/src/cid/cidload.c
index 26daa5da7f..a7da8ea39d 100644
--- a/thirdparty/freetype/src/cid/cidload.c
+++ b/thirdparty/freetype/src/cid/cidload.c
@@ -155,23 +155,24 @@
FT_CALLBACK_DEF( void )
- cid_parse_font_matrix( CID_Face face,
- CID_Parser* parser )
+ cid_parse_font_matrix( FT_Face face, /* CID_Face */
+ void* parser_ )
{
+ CID_Face cidface = (CID_Face)face;
+ CID_Parser* parser = (CID_Parser*)parser_;
CID_FaceDict dict;
- FT_Face root = (FT_Face)&face->root;
FT_Fixed temp[6];
FT_Fixed temp_scale;
- if ( parser->num_dict < face->cid.num_dicts )
+ if ( parser->num_dict < cidface->cid.num_dicts )
{
FT_Matrix* matrix;
FT_Vector* offset;
FT_Int result;
- dict = face->cid.font_dicts + parser->num_dict;
+ dict = cidface->cid.font_dicts + parser->num_dict;
matrix = &dict->font_matrix;
offset = &dict->font_offset;
@@ -204,7 +205,7 @@
if ( temp_scale != 0x10000L )
{
/* set units per EM based on FontMatrix values */
- root->units_per_EM = (FT_UShort)FT_DivFix( 1000, temp_scale );
+ face->units_per_EM = (FT_UShort)FT_DivFix( 1000, temp_scale );
temp[0] = FT_DivFix( temp[0], temp_scale );
temp[1] = FT_DivFix( temp[1], temp_scale );
@@ -237,13 +238,15 @@
FT_CALLBACK_DEF( void )
- parse_fd_array( CID_Face face,
- CID_Parser* parser )
+ parse_fd_array( FT_Face face, /* CID_Face */
+ void* parser_ )
{
- CID_FaceInfo cid = &face->cid;
- FT_Memory memory = face->root.memory;
- FT_Stream stream = parser->stream;
- FT_Error error = FT_Err_Ok;
+ CID_Face cidface = (CID_Face)face;
+ CID_Parser* parser = (CID_Parser*)parser_;
+ CID_FaceInfo cid = &cidface->cid;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_Stream stream = parser->stream;
+ FT_Error error = FT_Err_Ok;
FT_Long num_dicts, max_dicts;
@@ -313,18 +316,20 @@
/* By mistake, `expansion_factor' appears both in PS_PrivateRec */
/* and CID_FaceDictRec (both are public header files and can't */
- /* changed). We simply copy the value. */
+ /* be thus changed). We simply copy the value. */
FT_CALLBACK_DEF( void )
- parse_expansion_factor( CID_Face face,
- CID_Parser* parser )
+ parse_expansion_factor( FT_Face face, /* CID_Face */
+ void* parser_ )
{
+ CID_Face cidface = (CID_Face)face;
+ CID_Parser* parser = (CID_Parser*)parser_;
CID_FaceDict dict;
- if ( parser->num_dict < face->cid.num_dicts )
+ if ( parser->num_dict < cidface->cid.num_dicts )
{
- dict = face->cid.font_dicts + parser->num_dict;
+ dict = cidface->cid.font_dicts + parser->num_dict;
dict->expansion_factor = cid_parser_to_fixed( parser, 0 );
dict->private_dict.expansion_factor = dict->expansion_factor;
@@ -341,11 +346,15 @@
/* to catch it for producing better trace output. */
FT_CALLBACK_DEF( void )
- parse_font_name( CID_Face face,
- CID_Parser* parser )
+ parse_font_name( FT_Face face, /* CID_Face */
+ void* parser_ )
{
#ifdef FT_DEBUG_LEVEL_TRACE
- if ( parser->num_dict < face->cid.num_dicts )
+ CID_Face cidface = (CID_Face)face;
+ CID_Parser* parser = (CID_Parser*)parser_;
+
+
+ if ( parser->num_dict < cidface->cid.num_dicts )
{
T1_TokenRec token;
FT_UInt len;
@@ -361,7 +370,7 @@
}
#else
FT_UNUSED( face );
- FT_UNUSED( parser );
+ FT_UNUSED( parser_ );
#endif
return;
diff --git a/thirdparty/freetype/src/cid/cidobjs.c b/thirdparty/freetype/src/cid/cidobjs.c
index 06b2139a93..f698a41928 100644
--- a/thirdparty/freetype/src/cid/cidobjs.c
+++ b/thirdparty/freetype/src/cid/cidobjs.c
@@ -69,8 +69,7 @@
FT_Module module;
- module = FT_Get_Module( slot->face->driver->root.library,
- "pshinter" );
+ module = FT_Get_Module( slot->library, "pshinter" );
if ( module )
{
T1_Hints_Funcs funcs;
@@ -268,7 +267,8 @@
*
* @Input:
* stream ::
- * The source font stream.
+ * Dummy argument for compatibility with the `FT_Face_InitFunc` API.
+ * Ignored. The stream should be passed through `face->root.stream`.
*
* face_index ::
* The index of the font face in the resource.
@@ -375,6 +375,14 @@
if ( info->is_fixed_pitch )
cidface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
+ /*
+ * For the sfnt-wrapped CID fonts for MacOS, currently,
+ * its `cmap' tables are ignored, and the content in
+ * its `CID ' table is treated the same as naked CID-keyed
+ * font. See ft_lookup_PS_in_sfnt_stream().
+ */
+ cidface->face_flags |= FT_FACE_FLAG_CID_KEYED;
+
/* XXX: TODO: add kerning with .afm support */
/* get style name -- be careful, some broken fonts only */
diff --git a/thirdparty/freetype/src/cid/cidparse.c b/thirdparty/freetype/src/cid/cidparse.c
index 16889db9b6..171a886215 100644
--- a/thirdparty/freetype/src/cid/cidparse.c
+++ b/thirdparty/freetype/src/cid/cidparse.c
@@ -214,18 +214,24 @@
cur <= limit - STARTDATA_LEN &&
ft_strncmp( (char*)cur, STARTDATA, STARTDATA_LEN ) == 0 )
{
- if ( ft_strncmp( (char*)arg1, "(Hex)", 5 ) == 0 )
- {
- FT_Long tmp = ft_strtol( (const char *)arg2, NULL, 10 );
+ T1_TokenRec type_token;
+ FT_Long binary_length;
- if ( tmp < 0 )
+ parser->root.cursor = arg1;
+ cid_parser_to_token( parser, &type_token );
+ if ( type_token.limit - type_token.start == 5 &&
+ ft_memcmp( (char*)type_token.start, "(Hex)", 5 ) == 0 )
+ {
+ parser->root.cursor = arg2;
+ binary_length = cid_parser_to_int( parser );
+ if ( binary_length < 0 )
{
FT_ERROR(( "cid_parser_new: invalid length of hex data\n" ));
error = FT_THROW( Invalid_File_Format );
}
else
- parser->binary_length = (FT_ULong)tmp;
+ parser->binary_length = (FT_ULong)binary_length;
}
goto Exit;
diff --git a/thirdparty/freetype/src/cid/cidriver.c b/thirdparty/freetype/src/cid/cidriver.c
index f7499237d7..99e7b11839 100644
--- a/thirdparty/freetype/src/cid/cidriver.c
+++ b/thirdparty/freetype/src/cid/cidriver.c
@@ -48,10 +48,11 @@
*
*/
- static const char*
- cid_get_postscript_name( CID_Face face )
+ FT_CALLBACK_DEF( const char* )
+ cid_get_postscript_name( FT_Face face ) /* CID_Face */
{
- const char* result = face->cid.cid_font_name;
+ CID_Face cidface = (CID_Face)face;
+ const char* result = cidface->cid.cid_font_name;
if ( result && result[0] == '/' )
@@ -72,34 +73,36 @@
*
*/
- static FT_Error
- cid_ps_get_font_info( FT_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cid_ps_get_font_info( FT_Face face, /* CID_Face */
PS_FontInfoRec* afont_info )
{
- *afont_info = ((CID_Face)face)->cid.font_info;
+ *afont_info = ( (CID_Face)face )->cid.font_info;
return FT_Err_Ok;
}
- static FT_Error
- cid_ps_get_font_extra( FT_Face face,
- PS_FontExtraRec* afont_extra )
+
+ FT_CALLBACK_DEF( FT_Error )
+ cid_ps_get_font_extra( FT_Face face, /* CID_Face */
+ PS_FontExtraRec* afont_extra )
{
- *afont_extra = ((CID_Face)face)->font_extra;
+ *afont_extra = ( (CID_Face)face )->font_extra;
return FT_Err_Ok;
}
+
static const FT_Service_PsInfoRec cid_service_ps_info =
{
- (PS_GetFontInfoFunc) cid_ps_get_font_info, /* ps_get_font_info */
- (PS_GetFontExtraFunc) cid_ps_get_font_extra, /* ps_get_font_extra */
+ cid_ps_get_font_info, /* PS_GetFontInfoFunc ps_get_font_info */
+ cid_ps_get_font_extra, /* PS_GetFontExtraFunc ps_get_font_extra */
/* unsupported with CID fonts */
- (PS_HasGlyphNamesFunc) NULL, /* ps_has_glyph_names */
+ NULL, /* PS_HasGlyphNamesFunc ps_has_glyph_names */
/* unsupported */
- (PS_GetFontPrivateFunc)NULL, /* ps_get_font_private */
+ NULL, /* PS_GetFontPrivateFunc ps_get_font_private */
/* not implemented */
- (PS_GetFontValueFunc) NULL /* ps_get_font_value */
+ NULL /* PS_GetFontValueFunc ps_get_font_value */
};
@@ -107,13 +110,14 @@
* CID INFO SERVICE
*
*/
- static FT_Error
- cid_get_ros( CID_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cid_get_ros( FT_Face face, /* CID_Face */
const char* *registry,
const char* *ordering,
FT_Int *supplement )
{
- CID_FaceInfo cid = &face->cid;
+ CID_Face cidface = (CID_Face)face;
+ CID_FaceInfo cid = &cidface->cid;
if ( registry )
@@ -129,32 +133,48 @@
}
- static FT_Error
- cid_get_is_cid( CID_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cid_get_is_cid( FT_Face face, /* CID_Face */
FT_Bool *is_cid )
{
FT_Error error = FT_Err_Ok;
FT_UNUSED( face );
+ /*
+ * XXX: If the ROS is Adobe-Identity-H or -V,
+ * the font has no reliable information about
+ * its glyph collection. Should we not set
+ * *is_cid in such cases?
+ */
if ( is_cid )
- *is_cid = 1; /* cid driver is only used for CID keyed fonts */
+ *is_cid = 1;
return error;
}
- static FT_Error
- cid_get_cid_from_glyph_index( CID_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ cid_get_cid_from_glyph_index( FT_Face face, /* CID_Face */
FT_UInt glyph_index,
FT_UInt *cid )
{
- FT_Error error = FT_Err_Ok;
- FT_UNUSED( face );
-
-
- if ( cid )
- *cid = glyph_index; /* identity mapping */
+ FT_Error error = FT_Err_Ok;
+ CID_Face cidface = (CID_Face)face;
+
+
+ /*
+ * Currently, FreeType does not support incrementally-defined, CID-keyed
+ * fonts that store the glyph description data in a `/GlyphDirectory`
+ * array or dictionary. Fonts loaded by the incremental loading feature
+ * are thus not handled here.
+ */
+ error = cid_compute_fd_and_offsets( cidface, glyph_index,
+ NULL, NULL, NULL );
+ if ( error )
+ *cid = 0;
+ else
+ *cid = glyph_index;
return error;
}
@@ -162,12 +182,12 @@
static const FT_Service_CIDRec cid_service_cid_info =
{
- (FT_CID_GetRegistryOrderingSupplementFunc)
- cid_get_ros, /* get_ros */
- (FT_CID_GetIsInternallyCIDKeyedFunc)
- cid_get_is_cid, /* get_is_cid */
- (FT_CID_GetCIDFromGlyphIndexFunc)
- cid_get_cid_from_glyph_index /* get_cid_from_glyph_index */
+ cid_get_ros,
+ /* FT_CID_GetRegistryOrderingSupplementFunc get_ros */
+ cid_get_is_cid,
+ /* FT_CID_GetIsInternallyCIDKeyedFunc get_is_cid */
+ cid_get_cid_from_glyph_index
+ /* FT_CID_GetCIDFromGlyphIndexFunc get_cid_from_glyph_index */
};
@@ -179,9 +199,9 @@
FT_DEFINE_SERVICE_PROPERTIESREC(
cid_service_properties,
- (FT_Properties_SetFunc)ps_property_set, /* set_property */
- (FT_Properties_GetFunc)ps_property_get ) /* get_property */
-
+ ps_property_set, /* FT_Properties_SetFunc set_property */
+ ps_property_get /* FT_Properties_GetFunc get_property */
+ )
/*
* SERVICE LIST
@@ -209,7 +229,6 @@
}
-
FT_CALLBACK_TABLE_DEF
const FT_Driver_ClassRec t1cid_driver_class =
{
diff --git a/thirdparty/freetype/src/gxvalid/gxvfgen.c b/thirdparty/freetype/src/gxvalid/gxvfgen.c
index 1153542286..cf98bb36c3 100644
--- a/thirdparty/freetype/src/gxvalid/gxvfgen.c
+++ b/thirdparty/freetype/src/gxvalid/gxvfgen.c
@@ -97,7 +97,8 @@
#define EMPTYFEAT {0, 0, {NULL}}
- static GX_Feature_RegistryRec featreg_table[] = {
+ static GX_Feature_RegistryRec featreg_table[] =
+ {
{ /* 0 */
"All Typographic Features",
0,
diff --git a/thirdparty/freetype/src/gzip/ftgzip.c b/thirdparty/freetype/src/gzip/ftgzip.c
index 48da6ff9c7..ca6a2aabe6 100644
--- a/thirdparty/freetype/src/gzip/ftgzip.c
+++ b/thirdparty/freetype/src/gzip/ftgzip.c
@@ -70,10 +70,9 @@
/* so that configuration with `FT_CONFIG_OPTION_SYSTEM_ZLIB' might */
/* include the wrong `zconf.h' file, leading to errors. */
-#if defined( __GNUC__ ) || defined( __clang__ )
#define ZEXPORT
-#define ZEXTERN static
-#endif
+ /* prevent zlib functions from being visible outside their object files */
+#define ZEXTERN static
#define HAVE_MEMCPY 1
#define Z_SOLO 1
diff --git a/thirdparty/freetype/src/gzip/infback.c b/thirdparty/freetype/src/gzip/infback.c
deleted file mode 100644
index 264c14e0df..0000000000
--- a/thirdparty/freetype/src/gzip/infback.c
+++ /dev/null
@@ -1,644 +0,0 @@
-/* infback.c -- inflate using a call-back interface
- * Copyright (C) 1995-2022 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/*
- This code is largely copied from inflate.c. Normally either infback.o or
- inflate.o would be linked into an application--not both. The interface
- with inffast.c is retained so that optimized assembler-coded versions of
- inflate_fast() can be used with either inflate.c or infback.c.
- */
-
-#include "zutil.h"
-#include "inftrees.h"
-#include "inflate.h"
-#include "inffast.h"
-
-/* function prototypes */
-local void fixedtables OF((struct inflate_state FAR *state));
-
-/*
- strm provides memory allocation functions in zalloc and zfree, or
- Z_NULL to use the library memory allocation functions.
-
- windowBits is in the range 8..15, and window is a user-supplied
- window and output buffer that is 2**windowBits bytes.
- */
-int ZEXPORT inflateBackInit_(
- z_streamp strm,
- int windowBits,
- unsigned char FAR *window,
- const char *version,
- int stream_size)
-{
- struct inflate_state FAR *state;
-
- if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
- stream_size != (int)(sizeof(z_stream)))
- return Z_VERSION_ERROR;
- if (strm == Z_NULL || window == Z_NULL ||
- windowBits < 8 || windowBits > 15)
- return Z_STREAM_ERROR;
- strm->msg = Z_NULL; /* in case we return an error */
- if (strm->zalloc == (alloc_func)0) {
-#ifdef Z_SOLO
- return Z_STREAM_ERROR;
-#else
- strm->zalloc = zcalloc;
- strm->opaque = (voidpf)0;
-#endif
- }
- if (strm->zfree == (free_func)0)
-#ifdef Z_SOLO
- return Z_STREAM_ERROR;
-#else
- strm->zfree = zcfree;
-#endif
- state = (struct inflate_state FAR *)ZALLOC(strm, 1,
- sizeof(struct inflate_state));
- if (state == Z_NULL) return Z_MEM_ERROR;
- Tracev((stderr, "inflate: allocated\n"));
- strm->state = (struct internal_state FAR *)state;
- state->dmax = 32768U;
- state->wbits = (uInt)windowBits;
- state->wsize = 1U << windowBits;
- state->window = window;
- state->wnext = 0;
- state->whave = 0;
- state->sane = 1;
- return Z_OK;
-}
-
-/*
- Return state with length and distance decoding tables and index sizes set to
- fixed code decoding. Normally this returns fixed tables from inffixed.h.
- If BUILDFIXED is defined, then instead this routine builds the tables the
- first time it's called, and returns those tables the first time and
- thereafter. This reduces the size of the code by about 2K bytes, in
- exchange for a little execution time. However, BUILDFIXED should not be
- used for threaded applications, since the rewriting of the tables and virgin
- may not be thread-safe.
- */
-local void fixedtables(
- struct inflate_state FAR *state)
-{
-#ifdef BUILDFIXED
- static int virgin = 1;
- static code *lenfix, *distfix;
- static code fixed[544];
-
- /* build fixed huffman tables if first call (may not be thread safe) */
- if (virgin) {
- unsigned sym, bits;
- static code *next;
-
- /* literal/length table */
- sym = 0;
- while (sym < 144) state->lens[sym++] = 8;
- while (sym < 256) state->lens[sym++] = 9;
- while (sym < 280) state->lens[sym++] = 7;
- while (sym < 288) state->lens[sym++] = 8;
- next = fixed;
- lenfix = next;
- bits = 9;
- inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
-
- /* distance table */
- sym = 0;
- while (sym < 32) state->lens[sym++] = 5;
- distfix = next;
- bits = 5;
- inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
-
- /* do this just once */
- virgin = 0;
- }
-#else /* !BUILDFIXED */
-# include "inffixed.h"
-#endif /* BUILDFIXED */
- state->lencode = lenfix;
- state->lenbits = 9;
- state->distcode = distfix;
- state->distbits = 5;
-}
-
-/* Macros for inflateBack(): */
-
-/* Load returned state from inflate_fast() */
-#define LOAD() \
- do { \
- put = strm->next_out; \
- left = strm->avail_out; \
- next = strm->next_in; \
- have = strm->avail_in; \
- hold = state->hold; \
- bits = state->bits; \
- } while (0)
-
-/* Set state from registers for inflate_fast() */
-#define RESTORE() \
- do { \
- strm->next_out = put; \
- strm->avail_out = left; \
- strm->next_in = next; \
- strm->avail_in = have; \
- state->hold = hold; \
- state->bits = bits; \
- } while (0)
-
-/* Clear the input bit accumulator */
-#define INITBITS() \
- do { \
- hold = 0; \
- bits = 0; \
- } while (0)
-
-/* Assure that some input is available. If input is requested, but denied,
- then return a Z_BUF_ERROR from inflateBack(). */
-#define PULL() \
- do { \
- if (have == 0) { \
- have = in(in_desc, &next); \
- if (have == 0) { \
- next = Z_NULL; \
- ret = Z_BUF_ERROR; \
- goto inf_leave; \
- } \
- } \
- } while (0)
-
-/* Get a byte of input into the bit accumulator, or return from inflateBack()
- with an error if there is no input available. */
-#define PULLBYTE() \
- do { \
- PULL(); \
- have--; \
- hold += (unsigned long)(*next++) << bits; \
- bits += 8; \
- } while (0)
-
-/* Assure that there are at least n bits in the bit accumulator. If there is
- not enough available input to do that, then return from inflateBack() with
- an error. */
-#define NEEDBITS(n) \
- do { \
- while (bits < (unsigned)(n)) \
- PULLBYTE(); \
- } while (0)
-
-/* Return the low n bits of the bit accumulator (n < 16) */
-#define BITS(n) \
- ((unsigned)hold & ((1U << (n)) - 1))
-
-/* Remove n bits from the bit accumulator */
-#define DROPBITS(n) \
- do { \
- hold >>= (n); \
- bits -= (unsigned)(n); \
- } while (0)
-
-/* Remove zero to seven bits as needed to go to a byte boundary */
-#define BYTEBITS() \
- do { \
- hold >>= bits & 7; \
- bits -= bits & 7; \
- } while (0)
-
-/* Assure that some output space is available, by writing out the window
- if it's full. If the write fails, return from inflateBack() with a
- Z_BUF_ERROR. */
-#define ROOM() \
- do { \
- if (left == 0) { \
- put = state->window; \
- left = state->wsize; \
- state->whave = left; \
- if (out(out_desc, put, left)) { \
- ret = Z_BUF_ERROR; \
- goto inf_leave; \
- } \
- } \
- } while (0)
-
-/*
- strm provides the memory allocation functions and window buffer on input,
- and provides information on the unused input on return. For Z_DATA_ERROR
- returns, strm will also provide an error message.
-
- in() and out() are the call-back input and output functions. When
- inflateBack() needs more input, it calls in(). When inflateBack() has
- filled the window with output, or when it completes with data in the
- window, it calls out() to write out the data. The application must not
- change the provided input until in() is called again or inflateBack()
- returns. The application must not change the window/output buffer until
- inflateBack() returns.
-
- in() and out() are called with a descriptor parameter provided in the
- inflateBack() call. This parameter can be a structure that provides the
- information required to do the read or write, as well as accumulated
- information on the input and output such as totals and check values.
-
- in() should return zero on failure. out() should return non-zero on
- failure. If either in() or out() fails, than inflateBack() returns a
- Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it
- was in() or out() that caused in the error. Otherwise, inflateBack()
- returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format
- error, or Z_MEM_ERROR if it could not allocate memory for the state.
- inflateBack() can also return Z_STREAM_ERROR if the input parameters
- are not correct, i.e. strm is Z_NULL or the state was not initialized.
- */
-int ZEXPORT inflateBack(
- z_streamp strm,
- in_func in,
- void FAR *in_desc,
- out_func out,
- void FAR *out_desc)
-{
- struct inflate_state FAR *state;
- z_const unsigned char FAR *next; /* next input */
- unsigned char FAR *put; /* next output */
- unsigned have, left; /* available input and output */
- unsigned long hold; /* bit buffer */
- unsigned bits; /* bits in bit buffer */
- unsigned copy; /* number of stored or match bytes to copy */
- unsigned char FAR *from; /* where to copy match bytes from */
- code here; /* current decoding table entry */
- code last; /* parent table entry */
- unsigned len; /* length to copy for repeats, bits to drop */
- int ret; /* return code */
- static const unsigned short order[19] = /* permutation of code lengths */
- {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
-
- /* Check that the strm exists and that the state was initialized */
- if (strm == Z_NULL || strm->state == Z_NULL)
- return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
-
- /* Reset the state */
- strm->msg = Z_NULL;
- state->mode = TYPE;
- state->last = 0;
- state->whave = 0;
- next = strm->next_in;
- have = next != Z_NULL ? strm->avail_in : 0;
- hold = 0;
- bits = 0;
- put = state->window;
- left = state->wsize;
-
- /* Inflate until end of block marked as last */
- for (;;)
- switch (state->mode) {
- case TYPE:
- /* determine and dispatch block type */
- if (state->last) {
- BYTEBITS();
- state->mode = DONE;
- break;
- }
- NEEDBITS(3);
- state->last = BITS(1);
- DROPBITS(1);
- switch (BITS(2)) {
- case 0: /* stored block */
- Tracev((stderr, "inflate: stored block%s\n",
- state->last ? " (last)" : ""));
- state->mode = STORED;
- break;
- case 1: /* fixed block */
- fixedtables(state);
- Tracev((stderr, "inflate: fixed codes block%s\n",
- state->last ? " (last)" : ""));
- state->mode = LEN; /* decode codes */
- break;
- case 2: /* dynamic block */
- Tracev((stderr, "inflate: dynamic codes block%s\n",
- state->last ? " (last)" : ""));
- state->mode = TABLE;
- break;
- case 3:
- strm->msg = (char *)"invalid block type";
- state->mode = BAD;
- }
- DROPBITS(2);
- break;
-
- case STORED:
- /* get and verify stored block length */
- BYTEBITS(); /* go to byte boundary */
- NEEDBITS(32);
- if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
- strm->msg = (char *)"invalid stored block lengths";
- state->mode = BAD;
- break;
- }
- state->length = (unsigned)hold & 0xffff;
- Tracev((stderr, "inflate: stored length %u\n",
- state->length));
- INITBITS();
-
- /* copy stored block from input to output */
- while (state->length != 0) {
- copy = state->length;
- PULL();
- ROOM();
- if (copy > have) copy = have;
- if (copy > left) copy = left;
- zmemcpy(put, next, copy);
- have -= copy;
- next += copy;
- left -= copy;
- put += copy;
- state->length -= copy;
- }
- Tracev((stderr, "inflate: stored end\n"));
- state->mode = TYPE;
- break;
-
- case TABLE:
- /* get dynamic table entries descriptor */
- NEEDBITS(14);
- state->nlen = BITS(5) + 257;
- DROPBITS(5);
- state->ndist = BITS(5) + 1;
- DROPBITS(5);
- state->ncode = BITS(4) + 4;
- DROPBITS(4);
-#ifndef PKZIP_BUG_WORKAROUND
- if (state->nlen > 286 || state->ndist > 30) {
- strm->msg = (char *)"too many length or distance symbols";
- state->mode = BAD;
- break;
- }
-#endif
- Tracev((stderr, "inflate: table sizes ok\n"));
-
- /* get code length code lengths (not a typo) */
- state->have = 0;
- while (state->have < state->ncode) {
- NEEDBITS(3);
- state->lens[order[state->have++]] = (unsigned short)BITS(3);
- DROPBITS(3);
- }
- while (state->have < 19)
- state->lens[order[state->have++]] = 0;
- state->next = state->codes;
- state->lencode = (code const FAR *)(state->next);
- state->lenbits = 7;
- ret = inflate_table(CODES, state->lens, 19, &(state->next),
- &(state->lenbits), state->work);
- if (ret) {
- strm->msg = (char *)"invalid code lengths set";
- state->mode = BAD;
- break;
- }
- Tracev((stderr, "inflate: code lengths ok\n"));
-
- /* get length and distance code code lengths */
- state->have = 0;
- while (state->have < state->nlen + state->ndist) {
- for (;;) {
- here = state->lencode[BITS(state->lenbits)];
- if ((unsigned)(here.bits) <= bits) break;
- PULLBYTE();
- }
- if (here.val < 16) {
- DROPBITS(here.bits);
- state->lens[state->have++] = here.val;
- }
- else {
- if (here.val == 16) {
- NEEDBITS(here.bits + 2);
- DROPBITS(here.bits);
- if (state->have == 0) {
- strm->msg = (char *)"invalid bit length repeat";
- state->mode = BAD;
- break;
- }
- len = (unsigned)(state->lens[state->have - 1]);
- copy = 3 + BITS(2);
- DROPBITS(2);
- }
- else if (here.val == 17) {
- NEEDBITS(here.bits + 3);
- DROPBITS(here.bits);
- len = 0;
- copy = 3 + BITS(3);
- DROPBITS(3);
- }
- else {
- NEEDBITS(here.bits + 7);
- DROPBITS(here.bits);
- len = 0;
- copy = 11 + BITS(7);
- DROPBITS(7);
- }
- if (state->have + copy > state->nlen + state->ndist) {
- strm->msg = (char *)"invalid bit length repeat";
- state->mode = BAD;
- break;
- }
- while (copy--)
- state->lens[state->have++] = (unsigned short)len;
- }
- }
-
- /* handle error breaks in while */
- if (state->mode == BAD) break;
-
- /* check for end-of-block code (better have one) */
- if (state->lens[256] == 0) {
- strm->msg = (char *)"invalid code -- missing end-of-block";
- state->mode = BAD;
- break;
- }
-
- /* build code tables -- note: do not change the lenbits or distbits
- values here (9 and 6) without reading the comments in inftrees.h
- concerning the ENOUGH constants, which depend on those values */
- state->next = state->codes;
- state->lencode = (code const FAR *)(state->next);
- state->lenbits = 9;
- ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
- &(state->lenbits), state->work);
- if (ret) {
- strm->msg = (char *)"invalid literal/lengths set";
- state->mode = BAD;
- break;
- }
- state->distcode = (code const FAR *)(state->next);
- state->distbits = 6;
- ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
- &(state->next), &(state->distbits), state->work);
- if (ret) {
- strm->msg = (char *)"invalid distances set";
- state->mode = BAD;
- break;
- }
- Tracev((stderr, "inflate: codes ok\n"));
- state->mode = LEN;
- /* fallthrough */
-
- case LEN:
- /* use inflate_fast() if we have enough input and output */
- if (have >= 6 && left >= 258) {
- RESTORE();
- if (state->whave < state->wsize)
- state->whave = state->wsize - left;
- inflate_fast(strm, state->wsize);
- LOAD();
- break;
- }
-
- /* get a literal, length, or end-of-block code */
- for (;;) {
- here = state->lencode[BITS(state->lenbits)];
- if ((unsigned)(here.bits) <= bits) break;
- PULLBYTE();
- }
- if (here.op && (here.op & 0xf0) == 0) {
- last = here;
- for (;;) {
- here = state->lencode[last.val +
- (BITS(last.bits + last.op) >> last.bits)];
- if ((unsigned)(last.bits + here.bits) <= bits) break;
- PULLBYTE();
- }
- DROPBITS(last.bits);
- }
- DROPBITS(here.bits);
- state->length = (unsigned)here.val;
-
- /* process literal */
- if (here.op == 0) {
- Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
- "inflate: literal '%c'\n" :
- "inflate: literal 0x%02x\n", here.val));
- ROOM();
- *put++ = (unsigned char)(state->length);
- left--;
- state->mode = LEN;
- break;
- }
-
- /* process end of block */
- if (here.op & 32) {
- Tracevv((stderr, "inflate: end of block\n"));
- state->mode = TYPE;
- break;
- }
-
- /* invalid code */
- if (here.op & 64) {
- strm->msg = (char *)"invalid literal/length code";
- state->mode = BAD;
- break;
- }
-
- /* length code -- get extra bits, if any */
- state->extra = (unsigned)(here.op) & 15;
- if (state->extra != 0) {
- NEEDBITS(state->extra);
- state->length += BITS(state->extra);
- DROPBITS(state->extra);
- }
- Tracevv((stderr, "inflate: length %u\n", state->length));
-
- /* get distance code */
- for (;;) {
- here = state->distcode[BITS(state->distbits)];
- if ((unsigned)(here.bits) <= bits) break;
- PULLBYTE();
- }
- if ((here.op & 0xf0) == 0) {
- last = here;
- for (;;) {
- here = state->distcode[last.val +
- (BITS(last.bits + last.op) >> last.bits)];
- if ((unsigned)(last.bits + here.bits) <= bits) break;
- PULLBYTE();
- }
- DROPBITS(last.bits);
- }
- DROPBITS(here.bits);
- if (here.op & 64) {
- strm->msg = (char *)"invalid distance code";
- state->mode = BAD;
- break;
- }
- state->offset = (unsigned)here.val;
-
- /* get distance extra bits, if any */
- state->extra = (unsigned)(here.op) & 15;
- if (state->extra != 0) {
- NEEDBITS(state->extra);
- state->offset += BITS(state->extra);
- DROPBITS(state->extra);
- }
- if (state->offset > state->wsize - (state->whave < state->wsize ?
- left : 0)) {
- strm->msg = (char *)"invalid distance too far back";
- state->mode = BAD;
- break;
- }
- Tracevv((stderr, "inflate: distance %u\n", state->offset));
-
- /* copy match from window to output */
- do {
- ROOM();
- copy = state->wsize - state->offset;
- if (copy < left) {
- from = put + copy;
- copy = left - copy;
- }
- else {
- from = put - state->offset;
- copy = left;
- }
- if (copy > state->length) copy = state->length;
- state->length -= copy;
- left -= copy;
- do {
- *put++ = *from++;
- } while (--copy);
- } while (state->length != 0);
- break;
-
- case DONE:
- /* inflate stream terminated properly */
- ret = Z_STREAM_END;
- goto inf_leave;
-
- case BAD:
- ret = Z_DATA_ERROR;
- goto inf_leave;
-
- default:
- /* can't happen, but makes compilers happy */
- ret = Z_STREAM_ERROR;
- goto inf_leave;
- }
-
- /* Write leftover output and return unused input */
- inf_leave:
- if (left < state->wsize) {
- if (out(out_desc, state->window, state->wsize - left) &&
- ret == Z_STREAM_END)
- ret = Z_BUF_ERROR;
- }
- strm->next_in = next;
- strm->avail_in = have;
- return ret;
-}
-
-int ZEXPORT inflateBackEnd(
- z_streamp strm)
-{
- if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
- return Z_STREAM_ERROR;
- ZFREE(strm, strm->state);
- strm->state = Z_NULL;
- Tracev((stderr, "inflate: end\n"));
- return Z_OK;
-}
diff --git a/thirdparty/freetype/src/pcf/pcfdrivr.c b/thirdparty/freetype/src/pcf/pcfdrivr.c
index bfa6eacca4..f1dba02404 100644
--- a/thirdparty/freetype/src/pcf/pcfdrivr.c
+++ b/thirdparty/freetype/src/pcf/pcfdrivr.c
@@ -75,36 +75,36 @@ THE SOFTWARE.
FT_CALLBACK_DEF( FT_Error )
- pcf_cmap_init( FT_CMap pcfcmap, /* PCF_CMap */
+ pcf_cmap_init( FT_CMap cmap, /* PCF_CMap */
FT_Pointer init_data )
{
- PCF_CMap cmap = (PCF_CMap)pcfcmap;
- PCF_Face face = (PCF_Face)FT_CMAP_FACE( pcfcmap );
+ PCF_CMap pcfcmap = (PCF_CMap)cmap;
+ PCF_Face face = (PCF_Face)FT_CMAP_FACE( cmap );
FT_UNUSED( init_data );
- cmap->enc = &face->enc;
+ pcfcmap->enc = &face->enc;
return FT_Err_Ok;
}
FT_CALLBACK_DEF( void )
- pcf_cmap_done( FT_CMap pcfcmap ) /* PCF_CMap */
+ pcf_cmap_done( FT_CMap cmap ) /* PCF_CMap */
{
- PCF_CMap cmap = (PCF_CMap)pcfcmap;
+ PCF_CMap pcfcmap = (PCF_CMap)cmap;
- cmap->enc = NULL;
+ pcfcmap->enc = NULL;
}
FT_CALLBACK_DEF( FT_UInt )
- pcf_cmap_char_index( FT_CMap pcfcmap, /* PCF_CMap */
+ pcf_cmap_char_index( FT_CMap cmap, /* PCF_CMap */
FT_UInt32 charcode )
{
- PCF_Enc enc = ( (PCF_CMap)pcfcmap )->enc;
+ PCF_Enc enc = ( (PCF_CMap)cmap )->enc;
FT_UInt32 i = ( charcode >> 8 ) - enc->firstRow;
FT_UInt32 j = ( charcode & 0xFF ) - enc->firstCol;
@@ -121,10 +121,10 @@ THE SOFTWARE.
FT_CALLBACK_DEF( FT_UInt )
- pcf_cmap_char_next( FT_CMap pcfcmap, /* PCF_CMap */
+ pcf_cmap_char_next( FT_CMap cmap, /* PCF_CMap */
FT_UInt32 *acharcode )
{
- PCF_Enc enc = ( (PCF_CMap)pcfcmap )->enc;
+ PCF_Enc enc = ( (PCF_CMap)cmap )->enc;
FT_UInt32 charcode = *acharcode + 1;
FT_UInt32 i = ( charcode >> 8 ) - enc->firstRow;
@@ -170,9 +170,9 @@ THE SOFTWARE.
FT_CALLBACK_DEF( void )
- PCF_Face_Done( FT_Face pcfface ) /* PCF_Face */
+ PCF_Face_Done( FT_Face face ) /* PCF_Face */
{
- PCF_Face face = (PCF_Face)pcfface;
+ PCF_Face pcfface = (PCF_Face)face;
FT_Memory memory;
@@ -181,18 +181,18 @@ THE SOFTWARE.
memory = FT_FACE_MEMORY( face );
- FT_FREE( face->metrics );
- FT_FREE( face->enc.offset );
+ FT_FREE( pcfface->metrics );
+ FT_FREE( pcfface->enc.offset );
/* free properties */
- if ( face->properties )
+ if ( pcfface->properties )
{
FT_Int i;
- for ( i = 0; i < face->nprops; i++ )
+ for ( i = 0; i < pcfface->nprops; i++ )
{
- PCF_Property prop = &face->properties[i];
+ PCF_Property prop = &pcfface->properties[i];
if ( prop )
@@ -203,33 +203,33 @@ THE SOFTWARE.
}
}
- FT_FREE( face->properties );
+ FT_FREE( pcfface->properties );
}
- FT_FREE( face->toc.tables );
- FT_FREE( pcfface->family_name );
- FT_FREE( pcfface->style_name );
- FT_FREE( pcfface->available_sizes );
- FT_FREE( face->charset_encoding );
- FT_FREE( face->charset_registry );
+ FT_FREE( pcfface->toc.tables );
+ FT_FREE( face->family_name );
+ FT_FREE( face->style_name );
+ FT_FREE( face->available_sizes );
+ FT_FREE( pcfface->charset_encoding );
+ FT_FREE( pcfface->charset_registry );
/* close compressed stream if any */
- if ( pcfface->stream == &face->comp_stream )
+ if ( face->stream == &pcfface->comp_stream )
{
- FT_Stream_Close( &face->comp_stream );
- pcfface->stream = face->comp_source;
+ FT_Stream_Close( &pcfface->comp_stream );
+ face->stream = pcfface->comp_source;
}
}
FT_CALLBACK_DEF( FT_Error )
PCF_Face_Init( FT_Stream stream,
- FT_Face pcfface, /* PCF_Face */
+ FT_Face face, /* PCF_Face */
FT_Int face_index,
FT_Int num_params,
FT_Parameter* params )
{
- PCF_Face face = (PCF_Face)pcfface;
+ PCF_Face pcfface = (PCF_Face)face;
FT_Error error;
FT_UNUSED( num_params );
@@ -238,10 +238,10 @@ THE SOFTWARE.
FT_TRACE2(( "PCF driver\n" ));
- error = pcf_load_font( stream, face, face_index );
+ error = pcf_load_font( stream, pcfface, face_index );
if ( error )
{
- PCF_Face_Done( pcfface );
+ PCF_Face_Done( face );
#if defined( FT_CONFIG_OPTION_USE_ZLIB ) || \
defined( FT_CONFIG_OPTION_USE_LZW ) || \
@@ -254,7 +254,7 @@ THE SOFTWARE.
/* this didn't work, try gzip support! */
FT_TRACE2(( " ... try gzip stream\n" ));
- error2 = FT_Stream_OpenGzip( &face->comp_stream, stream );
+ error2 = FT_Stream_OpenGzip( &pcfface->comp_stream, stream );
if ( FT_ERR_EQ( error2, Unimplemented_Feature ) )
goto Fail;
@@ -270,7 +270,7 @@ THE SOFTWARE.
/* this didn't work, try LZW support! */
FT_TRACE2(( " ... try LZW stream\n" ));
- error3 = FT_Stream_OpenLZW( &face->comp_stream, stream );
+ error3 = FT_Stream_OpenLZW( &pcfface->comp_stream, stream );
if ( FT_ERR_EQ( error3, Unimplemented_Feature ) )
goto Fail;
@@ -286,7 +286,7 @@ THE SOFTWARE.
/* this didn't work, try Bzip2 support! */
FT_TRACE2(( " ... try Bzip2 stream\n" ));
- error4 = FT_Stream_OpenBzip2( &face->comp_stream, stream );
+ error4 = FT_Stream_OpenBzip2( &pcfface->comp_stream, stream );
if ( FT_ERR_EQ( error4, Unimplemented_Feature ) )
goto Fail;
@@ -297,12 +297,12 @@ THE SOFTWARE.
if ( error )
goto Fail;
- face->comp_source = stream;
- pcfface->stream = &face->comp_stream;
+ pcfface->comp_source = stream;
+ face->stream = &pcfface->comp_stream;
- stream = pcfface->stream;
+ stream = face->stream;
- error = pcf_load_font( stream, face, face_index );
+ error = pcf_load_font( stream, pcfface, face_index );
if ( error )
goto Fail;
@@ -326,14 +326,14 @@ THE SOFTWARE.
else if ( face_index > 0 && ( face_index & 0xFFFF ) > 0 )
{
FT_ERROR(( "PCF_Face_Init: invalid face index\n" ));
- PCF_Face_Done( pcfface );
+ PCF_Face_Done( face );
return FT_THROW( Invalid_Argument );
}
/* set up charmap */
{
- FT_String *charset_registry = face->charset_registry;
- FT_String *charset_encoding = face->charset_encoding;
+ FT_String *charset_registry = pcfface->charset_registry;
+ FT_String *charset_encoding = pcfface->charset_encoding;
FT_Bool unicode_charmap = 0;
@@ -349,13 +349,13 @@ THE SOFTWARE.
( s[2] == 'o' || s[2] == 'O' ) )
{
s += 3;
- if ( !ft_strcmp( s, "10646" ) ||
- ( !ft_strcmp( s, "8859" ) &&
- !ft_strcmp( face->charset_encoding, "1" ) ) )
+ if ( !ft_strcmp( s, "10646" ) ||
+ ( !ft_strcmp( s, "8859" ) &&
+ !ft_strcmp( pcfface->charset_encoding, "1" ) ) )
unicode_charmap = 1;
/* another name for ASCII */
- else if ( !ft_strcmp( s, "646.1991" ) &&
- !ft_strcmp( face->charset_encoding, "IRV" ) )
+ else if ( !ft_strcmp( s, "646.1991" ) &&
+ !ft_strcmp( pcfface->charset_encoding, "IRV" ) )
unicode_charmap = 1;
}
}
@@ -364,7 +364,7 @@ THE SOFTWARE.
FT_CharMapRec charmap;
- charmap.face = FT_FACE( face );
+ charmap.face = face;
charmap.encoding = FT_ENCODING_NONE;
/* initial platform/encoding should indicate unset status? */
charmap.platform_id = TT_PLATFORM_APPLE_UNICODE;
@@ -386,7 +386,7 @@ THE SOFTWARE.
Fail:
FT_TRACE2(( " not a PCF file\n" ));
- PCF_Face_Done( pcfface );
+ PCF_Face_Done( face );
error = FT_THROW( Unknown_File_Format ); /* error */
goto Exit;
}
@@ -569,15 +569,16 @@ THE SOFTWARE.
*
*/
- static FT_Error
- pcf_get_bdf_property( PCF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ pcf_get_bdf_property( FT_Face face, /* PCF_Face */
const char* prop_name,
BDF_PropertyRec *aproperty )
{
+ PCF_Face pcfface = (PCF_Face)face;
PCF_Property prop;
- prop = pcf_find_property( face, prop_name );
+ prop = pcf_find_property( pcfface, prop_name );
if ( prop )
{
if ( prop->isString )
@@ -611,13 +612,16 @@ THE SOFTWARE.
}
- static FT_Error
- pcf_get_charset_id( PCF_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ pcf_get_charset_id( FT_Face face, /* PCF_Face */
const char* *acharset_encoding,
const char* *acharset_registry )
{
- *acharset_encoding = face->charset_encoding;
- *acharset_registry = face->charset_registry;
+ PCF_Face pcfface = (PCF_Face)face;
+
+
+ *acharset_encoding = pcfface->charset_encoding;
+ *acharset_registry = pcfface->charset_registry;
return FT_Err_Ok;
}
@@ -634,7 +638,7 @@ THE SOFTWARE.
* PROPERTY SERVICE
*
*/
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
pcf_property_set( FT_Module module, /* PCF_Driver */
const char* property_name,
const void* value,
@@ -695,10 +699,10 @@ THE SOFTWARE.
}
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
pcf_property_get( FT_Module module, /* PCF_Driver */
const char* property_name,
- const void* value )
+ void* value )
{
#ifdef PCF_CONFIG_OPTION_LONG_FAMILY_NAMES
diff --git a/thirdparty/freetype/src/pfr/pfrcmap.c b/thirdparty/freetype/src/pfr/pfrcmap.c
index 312a9ffe17..08fe41d54e 100644
--- a/thirdparty/freetype/src/pfr/pfrcmap.c
+++ b/thirdparty/freetype/src/pfr/pfrcmap.c
@@ -24,17 +24,18 @@
FT_CALLBACK_DEF( FT_Error )
- pfr_cmap_init( PFR_CMap cmap,
+ pfr_cmap_init( FT_CMap cmap, /* PFR_CMap */
FT_Pointer pointer )
{
- FT_Error error = FT_Err_Ok;
- PFR_Face face = (PFR_Face)FT_CMAP_FACE( cmap );
+ PFR_CMap pfrcmap = (PFR_CMap)cmap;
+ FT_Error error = FT_Err_Ok;
+ PFR_Face face = (PFR_Face)FT_CMAP_FACE( cmap );
FT_UNUSED( pointer );
- cmap->num_chars = face->phy_font.num_chars;
- cmap->chars = face->phy_font.chars;
+ pfrcmap->num_chars = face->phy_font.num_chars;
+ pfrcmap->chars = face->phy_font.chars;
/* just for safety, check that the character entries are correctly */
/* sorted in increasing character code order */
@@ -42,9 +43,9 @@
FT_UInt n;
- for ( n = 1; n < cmap->num_chars; n++ )
+ for ( n = 1; n < pfrcmap->num_chars; n++ )
{
- if ( cmap->chars[n - 1].char_code >= cmap->chars[n].char_code )
+ if ( pfrcmap->chars[n - 1].char_code >= pfrcmap->chars[n].char_code )
{
error = FT_THROW( Invalid_Table );
goto Exit;
@@ -58,26 +59,30 @@
FT_CALLBACK_DEF( void )
- pfr_cmap_done( PFR_CMap cmap )
+ pfr_cmap_done( FT_CMap cmap ) /* PFR_CMap */
{
- cmap->chars = NULL;
- cmap->num_chars = 0;
+ PFR_CMap pfrcmap = (PFR_CMap)cmap;
+
+
+ pfrcmap->chars = NULL;
+ pfrcmap->num_chars = 0;
}
FT_CALLBACK_DEF( FT_UInt )
- pfr_cmap_char_index( PFR_CMap cmap,
+ pfr_cmap_char_index( FT_CMap cmap, /* PFR_CMap */
FT_UInt32 char_code )
{
- FT_UInt min = 0;
- FT_UInt max = cmap->num_chars;
- FT_UInt mid = min + ( max - min ) / 2;
+ PFR_CMap pfrcmap = (PFR_CMap)cmap;
+ FT_UInt min = 0;
+ FT_UInt max = pfrcmap->num_chars;
+ FT_UInt mid = min + ( max - min ) / 2;
PFR_Char gchar;
while ( min < max )
{
- gchar = cmap->chars + mid;
+ gchar = pfrcmap->chars + mid;
if ( gchar->char_code == char_code )
return mid + 1;
@@ -96,10 +101,11 @@
}
- FT_CALLBACK_DEF( FT_UInt32 )
- pfr_cmap_char_next( PFR_CMap cmap,
+ FT_CALLBACK_DEF( FT_UInt )
+ pfr_cmap_char_next( FT_CMap cmap, /* PFR_CMap */
FT_UInt32 *pchar_code )
{
+ PFR_CMap pfrcmap = (PFR_CMap)cmap;
FT_UInt result = 0;
FT_UInt32 char_code = *pchar_code + 1;
@@ -107,14 +113,14 @@
Restart:
{
FT_UInt min = 0;
- FT_UInt max = cmap->num_chars;
+ FT_UInt max = pfrcmap->num_chars;
FT_UInt mid = min + ( max - min ) / 2;
PFR_Char gchar;
while ( min < max )
{
- gchar = cmap->chars + mid;
+ gchar = pfrcmap->chars + mid;
if ( gchar->char_code == char_code )
{
@@ -143,9 +149,9 @@
/* we didn't find it, but we have a pair just above it */
char_code = 0;
- if ( min < cmap->num_chars )
+ if ( min < pfrcmap->num_chars )
{
- gchar = cmap->chars + min;
+ gchar = pfrcmap->chars + min;
result = min;
if ( result != 0 )
{
diff --git a/thirdparty/freetype/src/pfr/pfrdrivr.c b/thirdparty/freetype/src/pfr/pfrdrivr.c
index 78c6c6882c..0048f52411 100644
--- a/thirdparty/freetype/src/pfr/pfrdrivr.c
+++ b/thirdparty/freetype/src/pfr/pfrdrivr.c
@@ -27,16 +27,16 @@
FT_CALLBACK_DEF( FT_Error )
- pfr_get_kerning( FT_Face pfrface, /* PFR_Face */
+ pfr_get_kerning( FT_Face face, /* PFR_Face */
FT_UInt left,
FT_UInt right,
FT_Vector *avector )
{
- PFR_Face face = (PFR_Face)pfrface;
- PFR_PhyFont phys = &face->phy_font;
+ PFR_Face pfrface = (PFR_Face)face;
+ PFR_PhyFont phys = &pfrface->phy_font;
- (void)pfr_face_get_kerning( pfrface, left, right, avector );
+ (void)pfr_face_get_kerning( face, left, right, avector );
/* convert from metrics to outline units when necessary */
if ( phys->outline_resolution != phys->metrics_resolution )
@@ -62,12 +62,12 @@
*/
FT_CALLBACK_DEF( FT_Error )
- pfr_get_advance( FT_Face pfrface, /* PFR_Face */
+ pfr_get_advance( FT_Face face, /* PFR_Face */
FT_UInt gindex,
FT_Pos *anadvance )
{
- PFR_Face face = (PFR_Face)pfrface;
- FT_Error error = FT_ERR( Invalid_Argument );
+ PFR_Face pfrface = (PFR_Face)face;
+ FT_Error error = FT_ERR( Invalid_Argument );
*anadvance = 0;
@@ -77,9 +77,9 @@
gindex--;
- if ( face )
+ if ( pfrface )
{
- PFR_PhyFont phys = &face->phy_font;
+ PFR_PhyFont phys = &pfrface->phy_font;
if ( gindex < phys->num_chars )
@@ -95,16 +95,16 @@
FT_CALLBACK_DEF( FT_Error )
- pfr_get_metrics( FT_Face pfrface, /* PFR_Face */
+ pfr_get_metrics( FT_Face face, /* PFR_Face */
FT_UInt *anoutline_resolution,
FT_UInt *ametrics_resolution,
FT_Fixed *ametrics_x_scale,
FT_Fixed *ametrics_y_scale )
{
- PFR_Face face = (PFR_Face)pfrface;
- PFR_PhyFont phys = &face->phy_font;
+ PFR_Face pfrface = (PFR_Face)face;
+ PFR_PhyFont phys = &pfrface->phy_font;
FT_Fixed x_scale, y_scale;
- FT_Size size = face->root.size;
+ FT_Size size = pfrface->root.size;
if ( anoutline_resolution )
diff --git a/thirdparty/freetype/src/pfr/pfrgload.c b/thirdparty/freetype/src/pfr/pfrgload.c
index 14f2ec3778..48cf27ec80 100644
--- a/thirdparty/freetype/src/pfr/pfrgload.c
+++ b/thirdparty/freetype/src/pfr/pfrgload.c
@@ -560,8 +560,7 @@
FT_Byte* limit )
{
FT_Error error = FT_Err_Ok;
- FT_GlyphLoader loader = glyph->loader;
- FT_Memory memory = loader->memory;
+ FT_Memory memory = glyph->loader->memory;
PFR_SubGlyph subglyph;
FT_UInt flags, i, count, org_count;
FT_Int x_pos, y_pos;
diff --git a/thirdparty/freetype/src/pfr/pfrload.c b/thirdparty/freetype/src/pfr/pfrload.c
index de85ee6aad..856a5942f5 100644
--- a/thirdparty/freetype/src/pfr/pfrload.c
+++ b/thirdparty/freetype/src/pfr/pfrload.c
@@ -449,15 +449,16 @@
/* load bitmap strikes lists */
FT_CALLBACK_DEF( FT_Error )
- pfr_extra_item_load_bitmap_info( FT_Byte* p,
- FT_Byte* limit,
- PFR_PhyFont phy_font )
+ pfr_extra_item_load_bitmap_info( FT_Byte* p,
+ FT_Byte* limit,
+ void* phy_font_ )
{
- FT_Memory memory = phy_font->memory;
- PFR_Strike strike;
- FT_UInt flags0;
- FT_UInt n, count, size1;
- FT_Error error = FT_Err_Ok;
+ PFR_PhyFont phy_font = (PFR_PhyFont)phy_font_;
+ FT_Memory memory = phy_font->memory;
+ PFR_Strike strike;
+ FT_UInt flags0;
+ FT_UInt n, count, size1;
+ FT_Error error = FT_Err_Ok;
PFR_CHECK( 5 );
@@ -549,13 +550,14 @@
* family.
*/
FT_CALLBACK_DEF( FT_Error )
- pfr_extra_item_load_font_id( FT_Byte* p,
- FT_Byte* limit,
- PFR_PhyFont phy_font )
+ pfr_extra_item_load_font_id( FT_Byte* p,
+ FT_Byte* limit,
+ void* phy_font_ )
{
- FT_Error error = FT_Err_Ok;
- FT_Memory memory = phy_font->memory;
- FT_UInt len = (FT_UInt)( limit - p );
+ PFR_PhyFont phy_font = (PFR_PhyFont)phy_font_;
+ FT_Error error = FT_Err_Ok;
+ FT_Memory memory = phy_font->memory;
+ FT_UInt len = (FT_UInt)( limit - p );
if ( phy_font->font_id )
@@ -575,14 +577,15 @@
/* load stem snap tables */
FT_CALLBACK_DEF( FT_Error )
- pfr_extra_item_load_stem_snaps( FT_Byte* p,
- FT_Byte* limit,
- PFR_PhyFont phy_font )
+ pfr_extra_item_load_stem_snaps( FT_Byte* p,
+ FT_Byte* limit,
+ void* phy_font_ )
{
- FT_UInt count, num_vert, num_horz;
- FT_Int* snaps = NULL;
- FT_Error error = FT_Err_Ok;
- FT_Memory memory = phy_font->memory;
+ PFR_PhyFont phy_font = (PFR_PhyFont)phy_font_;
+ FT_UInt count, num_vert, num_horz;
+ FT_Int* snaps = NULL;
+ FT_Error error = FT_Err_Ok;
+ FT_Memory memory = phy_font->memory;
if ( phy_font->vertical.stem_snaps )
@@ -619,10 +622,11 @@
/* load kerning pair data */
FT_CALLBACK_DEF( FT_Error )
- pfr_extra_item_load_kerning_pairs( FT_Byte* p,
- FT_Byte* limit,
- PFR_PhyFont phy_font )
+ pfr_extra_item_load_kerning_pairs( FT_Byte* p,
+ FT_Byte* limit,
+ void* phy_font_ )
{
+ PFR_PhyFont phy_font = (PFR_PhyFont)phy_font_;
PFR_KernItem item = NULL;
FT_Error error = FT_Err_Ok;
FT_Memory memory = phy_font->memory;
@@ -715,10 +719,10 @@
static const PFR_ExtraItemRec pfr_phy_font_extra_items[] =
{
- { 1, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_bitmap_info },
- { 2, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_font_id },
- { 3, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_stem_snaps },
- { 4, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_kerning_pairs },
+ { 1, pfr_extra_item_load_bitmap_info },
+ { 2, pfr_extra_item_load_font_id },
+ { 3, pfr_extra_item_load_stem_snaps },
+ { 4, pfr_extra_item_load_kerning_pairs },
{ 0, NULL }
};
diff --git a/thirdparty/freetype/src/pfr/pfrobjs.c b/thirdparty/freetype/src/pfr/pfrobjs.c
index 3db8f0a060..8ef17c6636 100644
--- a/thirdparty/freetype/src/pfr/pfrobjs.c
+++ b/thirdparty/freetype/src/pfr/pfrobjs.c
@@ -50,14 +50,14 @@
if ( !face )
return;
- memory = pfrface->driver->root.memory;
+ memory = pfrface->memory;
/* we don't want dangling pointers */
pfrface->family_name = NULL;
pfrface->style_name = NULL;
/* finalize the physical font record */
- pfr_phy_font_done( &face->phy_font, FT_FACE_MEMORY( face ) );
+ pfr_phy_font_done( &face->phy_font, memory );
/* no need to finalize the logical font or the header */
FT_FREE( pfrface->available_sizes );
@@ -214,7 +214,7 @@
FT_UInt n, count = phy_font->num_strikes;
FT_Bitmap_Size* size;
PFR_Strike strike;
- FT_Memory memory = pfrface->stream->memory;
+ FT_Memory memory = pfrface->memory;
if ( FT_QNEW_ARRAY( pfrface->available_sizes, count ) )
diff --git a/thirdparty/freetype/src/psaux/afmparse.c b/thirdparty/freetype/src/psaux/afmparse.c
index 68f95698e6..db08941def 100644
--- a/thirdparty/freetype/src/psaux/afmparse.c
+++ b/thirdparty/freetype/src/psaux/afmparse.c
@@ -1086,7 +1086,7 @@
#else /* T1_CONFIG_OPTION_NO_AFM */
/* ANSI C doesn't like empty source files */
- typedef int _afm_parse_dummy;
+ typedef int afm_parse_dummy_;
#endif /* T1_CONFIG_OPTION_NO_AFM */
diff --git a/thirdparty/freetype/src/psaux/t1cmap.c b/thirdparty/freetype/src/psaux/t1cmap.c
index bf0a393b45..c4bcf599ea 100644
--- a/thirdparty/freetype/src/psaux/t1cmap.c
+++ b/thirdparty/freetype/src/psaux/t1cmap.c
@@ -50,8 +50,11 @@
FT_CALLBACK_DEF( void )
- t1_cmap_std_done( T1_CMapStd cmap )
+ t1_cmap_std_done( FT_CMap cmap_ ) /* T1_CMapStd */
{
+ T1_CMapStd cmap = (T1_CMapStd)cmap_;
+
+
cmap->num_glyphs = 0;
cmap->glyph_names = NULL;
cmap->sid_to_string = NULL;
@@ -60,10 +63,11 @@
FT_CALLBACK_DEF( FT_UInt )
- t1_cmap_std_char_index( T1_CMapStd cmap,
- FT_UInt32 char_code )
+ t1_cmap_std_char_index( FT_CMap cmap, /* T1_CMapStd */
+ FT_UInt32 char_code )
{
- FT_UInt result = 0;
+ T1_CMapStd t1cmap = (T1_CMapStd)cmap;
+ FT_UInt result = 0;
if ( char_code < 256 )
@@ -73,13 +77,13 @@
/* convert character code to Adobe SID string */
- code = cmap->code_to_sid[char_code];
- glyph_name = cmap->sid_to_string( code );
+ code = t1cmap->code_to_sid[char_code];
+ glyph_name = t1cmap->sid_to_string( code );
/* look for the corresponding glyph name */
- for ( n = 0; n < cmap->num_glyphs; n++ )
+ for ( n = 0; n < t1cmap->num_glyphs; n++ )
{
- const char* gname = cmap->glyph_names[n];
+ const char* gname = t1cmap->glyph_names[n];
if ( gname && gname[0] == glyph_name[0] &&
@@ -95,9 +99,9 @@
}
- FT_CALLBACK_DEF( FT_UInt32 )
- t1_cmap_std_char_next( T1_CMapStd cmap,
- FT_UInt32 *pchar_code )
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_std_char_next( FT_CMap cmap,
+ FT_UInt32 *pchar_code )
{
FT_UInt result = 0;
FT_UInt32 char_code = *pchar_code + 1;
@@ -120,13 +124,14 @@
FT_CALLBACK_DEF( FT_Error )
- t1_cmap_standard_init( T1_CMapStd cmap,
+ t1_cmap_standard_init( FT_CMap cmap, /* T1_CMapStd */
FT_Pointer pointer )
{
+ T1_CMapStd t1cmap = (T1_CMapStd)cmap;
FT_UNUSED( pointer );
- t1_cmap_std_init( cmap, 0 );
+ t1_cmap_std_init( t1cmap, 0 );
return 0;
}
@@ -150,13 +155,14 @@
FT_CALLBACK_DEF( FT_Error )
- t1_cmap_expert_init( T1_CMapStd cmap,
+ t1_cmap_expert_init( FT_CMap cmap, /* T1_CMapStd */
FT_Pointer pointer )
{
+ T1_CMapStd t1cmap = (T1_CMapStd)cmap;
FT_UNUSED( pointer );
- t1_cmap_std_init( cmap, 1 );
+ t1_cmap_std_init( t1cmap, 1 );
return 0;
}
@@ -188,20 +194,21 @@
FT_CALLBACK_DEF( FT_Error )
- t1_cmap_custom_init( T1_CMapCustom cmap,
- FT_Pointer pointer )
+ t1_cmap_custom_init( FT_CMap cmap, /* T1_CMapCustom */
+ FT_Pointer pointer )
{
- T1_Face face = (T1_Face)FT_CMAP_FACE( cmap );
- T1_Encoding encoding = &face->type1.encoding;
+ T1_CMapCustom t1cmap = (T1_CMapCustom)cmap;
+ T1_Face face = (T1_Face)FT_CMAP_FACE( cmap );
+ T1_Encoding encoding = &face->type1.encoding;
FT_UNUSED( pointer );
- cmap->first = (FT_UInt)encoding->code_first;
- cmap->count = (FT_UInt)encoding->code_last - cmap->first;
- cmap->indices = encoding->char_index;
+ t1cmap->first = (FT_UInt)encoding->code_first;
+ t1cmap->count = (FT_UInt)encoding->code_last - t1cmap->first;
+ t1cmap->indices = encoding->char_index;
- FT_ASSERT( cmap->indices );
+ FT_ASSERT( t1cmap->indices );
FT_ASSERT( encoding->code_first <= encoding->code_last );
return 0;
@@ -209,45 +216,50 @@
FT_CALLBACK_DEF( void )
- t1_cmap_custom_done( T1_CMapCustom cmap )
+ t1_cmap_custom_done( FT_CMap cmap ) /* T1_CMapCustom */
{
- cmap->indices = NULL;
- cmap->first = 0;
- cmap->count = 0;
+ T1_CMapCustom t1cmap = (T1_CMapCustom)cmap;
+
+
+ t1cmap->indices = NULL;
+ t1cmap->first = 0;
+ t1cmap->count = 0;
}
FT_CALLBACK_DEF( FT_UInt )
- t1_cmap_custom_char_index( T1_CMapCustom cmap,
- FT_UInt32 char_code )
+ t1_cmap_custom_char_index( FT_CMap cmap, /* T1_CMapCustom */
+ FT_UInt32 char_code )
{
- FT_UInt result = 0;
+ T1_CMapCustom t1cmap = (T1_CMapCustom)cmap;
+ FT_UInt result = 0;
- if ( ( char_code >= cmap->first ) &&
- ( char_code < ( cmap->first + cmap->count ) ) )
- result = cmap->indices[char_code];
+ if ( char_code >= t1cmap->first &&
+ char_code < ( t1cmap->first + t1cmap->count ) )
+ result = t1cmap->indices[char_code];
return result;
}
- FT_CALLBACK_DEF( FT_UInt32 )
- t1_cmap_custom_char_next( T1_CMapCustom cmap,
- FT_UInt32 *pchar_code )
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_custom_char_next( FT_CMap cmap, /* T1_CMapCustom */
+ FT_UInt32 *pchar_code )
{
- FT_UInt result = 0;
- FT_UInt32 char_code = *pchar_code;
+ T1_CMapCustom t1cmap = (T1_CMapCustom)cmap;
+ FT_UInt result = 0;
+ FT_UInt32 char_code = *pchar_code;
char_code++;
- if ( char_code < cmap->first )
- char_code = cmap->first;
+ if ( char_code < t1cmap->first )
+ char_code = t1cmap->first;
- for ( ; char_code < ( cmap->first + cmap->count ); char_code++ )
+ for ( ; char_code < ( t1cmap->first + t1cmap->count ); char_code++ )
{
- result = cmap->indices[char_code];
+ result = t1cmap->indices[char_code];
if ( result != 0 )
goto Exit;
}
@@ -287,20 +299,24 @@
/*************************************************************************/
FT_CALLBACK_DEF( const char * )
- psaux_get_glyph_name( T1_Face face,
+ psaux_get_glyph_name( void* face_,
FT_UInt idx )
{
+ T1_Face face = (T1_Face)face_;
+
+
return face->type1.glyph_names[idx];
}
FT_CALLBACK_DEF( FT_Error )
- t1_cmap_unicode_init( PS_Unicodes unicodes,
- FT_Pointer pointer )
+ t1_cmap_unicode_init( FT_CMap cmap, /* PS_Unicodes */
+ FT_Pointer pointer )
{
- T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes );
- FT_Memory memory = FT_FACE_MEMORY( face );
- FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ T1_Face face = (T1_Face)FT_CMAP_FACE( cmap );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
FT_UNUSED( pointer );
@@ -311,17 +327,18 @@
return psnames->unicodes_init( memory,
unicodes,
(FT_UInt)face->type1.num_glyphs,
- (PS_GetGlyphNameFunc)&psaux_get_glyph_name,
+ &psaux_get_glyph_name,
(PS_FreeGlyphNameFunc)NULL,
(FT_Pointer)face );
}
FT_CALLBACK_DEF( void )
- t1_cmap_unicode_done( PS_Unicodes unicodes )
+ t1_cmap_unicode_done( FT_CMap cmap ) /* PS_Unicodes */
{
- FT_Face face = FT_CMAP_FACE( unicodes );
- FT_Memory memory = FT_FACE_MEMORY( face );
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ FT_Face face = FT_CMAP_FACE( cmap );
+ FT_Memory memory = FT_FACE_MEMORY( face );
FT_FREE( unicodes->maps );
@@ -330,23 +347,25 @@
FT_CALLBACK_DEF( FT_UInt )
- t1_cmap_unicode_char_index( PS_Unicodes unicodes,
- FT_UInt32 char_code )
+ t1_cmap_unicode_char_index( FT_CMap cmap, /* PS_Unicodes */
+ FT_UInt32 char_code )
{
- T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes );
- FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ T1_Face face = (T1_Face)FT_CMAP_FACE( cmap );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
return psnames->unicodes_char_index( unicodes, char_code );
}
- FT_CALLBACK_DEF( FT_UInt32 )
- t1_cmap_unicode_char_next( PS_Unicodes unicodes,
- FT_UInt32 *pchar_code )
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_unicode_char_next( FT_CMap cmap, /* PS_Unicodes */
+ FT_UInt32 *pchar_code )
{
- T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes );
- FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ T1_Face face = (T1_Face)FT_CMAP_FACE( cmap );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
return psnames->unicodes_char_next( unicodes, pchar_code );
diff --git a/thirdparty/freetype/src/pshinter/pshalgo.c b/thirdparty/freetype/src/pshinter/pshalgo.c
index a7f321291a..4f622e1e44 100644
--- a/thirdparty/freetype/src/pshinter/pshalgo.c
+++ b/thirdparty/freetype/src/pshinter/pshalgo.c
@@ -516,7 +516,7 @@
if ( !psh_hint_is_fitted( parent ) )
psh_hint_align( parent, globals, dimension, glyph );
- /* keep original relation between hints, this is, use the */
+ /* keep original relation between hints, that is, use the */
/* scaled distance between the centers of the hints to */
/* compute the new position */
par_org_center = parent->org_pos + ( parent->org_len >> 1 );
diff --git a/thirdparty/freetype/src/pshinter/pshmod.c b/thirdparty/freetype/src/pshinter/pshmod.c
index a12e485660..974a99e018 100644
--- a/thirdparty/freetype/src/pshinter/pshmod.c
+++ b/thirdparty/freetype/src/pshinter/pshmod.c
@@ -37,8 +37,11 @@
/* finalize module */
FT_CALLBACK_DEF( void )
- ps_hinter_done( PS_Hinter_Module module )
+ ps_hinter_done( FT_Module module_ ) /* PS_Hinter_Module */
{
+ PS_Hinter_Module module = (PS_Hinter_Module)module_;
+
+
module->t1_funcs.hints = NULL;
module->t2_funcs.hints = NULL;
@@ -48,8 +51,10 @@
/* initialize module, create hints recorder and the interface */
FT_CALLBACK_DEF( FT_Error )
- ps_hinter_init( PS_Hinter_Module module )
+ ps_hinter_init( FT_Module module_ ) /* PS_Hinter_Module */
{
+ PS_Hinter_Module module = (PS_Hinter_Module)module_;
+
FT_Memory memory = module->root.memory;
void* ph = &module->ps_hints;
diff --git a/thirdparty/freetype/src/pshinter/pshrec.c b/thirdparty/freetype/src/pshinter/pshrec.c
index 58c8cf1b48..680e6d0135 100644
--- a/thirdparty/freetype/src/pshinter/pshrec.c
+++ b/thirdparty/freetype/src/pshinter/pshrec.c
@@ -851,10 +851,11 @@
/* add one Type1 counter stem to the current hints table */
static void
- ps_hints_t1stem3( PS_Hints hints,
+ ps_hints_t1stem3( T1_Hints hints_, /* PS_Hints */
FT_UInt dimension,
FT_Fixed* stems )
{
+ PS_Hints hints = (PS_Hints)hints_;
FT_Error error = FT_Err_Ok;
@@ -914,9 +915,10 @@
/* reset hints (only with Type 1 hints) */
static void
- ps_hints_t1reset( PS_Hints hints,
+ ps_hints_t1reset( T1_Hints hints_, /* PS_Hints */
FT_UInt end_point )
{
+ PS_Hints hints = (PS_Hints)hints_;
FT_Error error = FT_Err_Ok;
@@ -953,11 +955,12 @@
/* Type2 "hintmask" operator, add a new hintmask to each direction */
static void
- ps_hints_t2mask( PS_Hints hints,
+ ps_hints_t2mask( T2_Hints hints_, /* PS_Hints */
FT_UInt end_point,
FT_UInt bit_count,
const FT_Byte* bytes )
{
+ PS_Hints hints = (PS_Hints)hints_;
FT_Error error;
@@ -999,10 +1002,11 @@
static void
- ps_hints_t2counter( PS_Hints hints,
+ ps_hints_t2counter( T2_Hints hints_, /* PS_Hints */
FT_UInt bit_count,
const FT_Byte* bytes )
{
+ PS_Hints hints = (PS_Hints)hints_;
FT_Error error;
@@ -1087,6 +1091,13 @@
ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_1 );
}
+ static FT_Error
+ t1_hints_close( T1_Hints hints,
+ FT_UInt end_point )
+ {
+ return ps_hints_close( (PS_Hints)hints, end_point );
+ }
+
static void
t1_hints_stem( T1_Hints hints,
FT_UInt dimension,
@@ -1102,17 +1113,27 @@
}
+ static FT_Error
+ t1_hints_apply( T1_Hints hints,
+ FT_Outline* outline,
+ PSH_Globals globals,
+ FT_Render_Mode hint_mode )
+ {
+ return ps_hints_apply( (PS_Hints)hints, outline, globals, hint_mode );
+ }
+
+
FT_LOCAL_DEF( void )
t1_hints_funcs_init( T1_Hints_FuncsRec* funcs )
{
FT_ZERO( funcs );
funcs->open = (T1_Hints_OpenFunc) t1_hints_open;
- funcs->close = (T1_Hints_CloseFunc) ps_hints_close;
+ funcs->close = (T1_Hints_CloseFunc) t1_hints_close;
funcs->stem = (T1_Hints_SetStemFunc) t1_hints_stem;
funcs->stem3 = (T1_Hints_SetStem3Func)ps_hints_t1stem3;
funcs->reset = (T1_Hints_ResetFunc) ps_hints_t1reset;
- funcs->apply = (T1_Hints_ApplyFunc) ps_hints_apply;
+ funcs->apply = (T1_Hints_ApplyFunc) t1_hints_apply;
}
@@ -1131,6 +1152,14 @@
}
+ static FT_Error
+ t2_hints_close( T2_Hints hints,
+ FT_UInt end_point )
+ {
+ return ps_hints_close( (PS_Hints)hints, end_point );
+ }
+
+
static void
t2_hints_stems( T2_Hints hints,
FT_UInt dimension,
@@ -1168,17 +1197,27 @@
}
+ static FT_Error
+ t2_hints_apply( T2_Hints hints,
+ FT_Outline* outline,
+ PSH_Globals globals,
+ FT_Render_Mode hint_mode )
+ {
+ return ps_hints_apply( (PS_Hints)hints, outline, globals, hint_mode );
+ }
+
+
FT_LOCAL_DEF( void )
t2_hints_funcs_init( T2_Hints_FuncsRec* funcs )
{
FT_ZERO( funcs );
- funcs->open = (T2_Hints_OpenFunc) t2_hints_open;
- funcs->close = (T2_Hints_CloseFunc) ps_hints_close;
- funcs->stems = (T2_Hints_StemsFunc) t2_hints_stems;
- funcs->hintmask= (T2_Hints_MaskFunc) ps_hints_t2mask;
- funcs->counter = (T2_Hints_CounterFunc)ps_hints_t2counter;
- funcs->apply = (T2_Hints_ApplyFunc) ps_hints_apply;
+ funcs->open = (T2_Hints_OpenFunc) t2_hints_open;
+ funcs->close = (T2_Hints_CloseFunc) t2_hints_close;
+ funcs->stems = (T2_Hints_StemsFunc) t2_hints_stems;
+ funcs->hintmask = (T2_Hints_MaskFunc) ps_hints_t2mask;
+ funcs->counter = (T2_Hints_CounterFunc)ps_hints_t2counter;
+ funcs->apply = (T2_Hints_ApplyFunc) t2_hints_apply;
}
diff --git a/thirdparty/freetype/src/psnames/psmodule.c b/thirdparty/freetype/src/psnames/psmodule.c
index db454e558e..8203a0465d 100644
--- a/thirdparty/freetype/src/psnames/psmodule.c
+++ b/thirdparty/freetype/src/psnames/psmodule.c
@@ -57,7 +57,7 @@
/* the name, as in `A.swash' or `e.final'; in this case, the */
/* VARIANT_BIT is set in the return value. */
/* */
- static FT_UInt32
+ FT_CALLBACK_DEF( FT_UInt32 )
ps_unicode_value( const char* glyph_name )
{
/* If the name begins with `uni', then the glyph name may be a */
@@ -309,7 +309,7 @@
/* Build a table that maps Unicode values to glyph indices. */
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
ps_unicodes_init( FT_Memory memory,
PS_Unicodes table,
FT_UInt num_glyphs,
@@ -408,7 +408,7 @@
}
- static FT_UInt
+ FT_CALLBACK_DEF( FT_UInt )
ps_unicodes_char_index( PS_Unicodes table,
FT_UInt32 unicode )
{
@@ -453,7 +453,7 @@
}
- static FT_UInt32
+ FT_CALLBACK_DEF( FT_UInt )
ps_unicodes_char_next( PS_Unicodes table,
FT_UInt32 *unicode )
{
@@ -518,7 +518,7 @@
#endif /* FT_CONFIG_OPTION_ADOBE_GLYPH_LIST */
- static const char*
+ FT_CALLBACK_DEF( const char* )
ps_get_macintosh_name( FT_UInt name_index )
{
if ( name_index >= FT_NUM_MAC_NAMES )
@@ -528,7 +528,7 @@
}
- static const char*
+ FT_CALLBACK_DEF( const char* )
ps_get_standard_strings( FT_UInt sid )
{
if ( sid >= FT_NUM_SID_NAMES )
@@ -543,13 +543,13 @@
FT_DEFINE_SERVICE_PSCMAPSREC(
pscmaps_interface,
- (PS_Unicode_ValueFunc) ps_unicode_value, /* unicode_value */
- (PS_Unicodes_InitFunc) ps_unicodes_init, /* unicodes_init */
- (PS_Unicodes_CharIndexFunc)ps_unicodes_char_index, /* unicodes_char_index */
- (PS_Unicodes_CharNextFunc) ps_unicodes_char_next, /* unicodes_char_next */
+ ps_unicode_value, /* PS_Unicode_ValueFunc unicode_value */
+ ps_unicodes_init, /* PS_Unicodes_InitFunc unicodes_init */
+ ps_unicodes_char_index, /* PS_Unicodes_CharIndexFunc unicodes_char_index */
+ ps_unicodes_char_next, /* PS_Unicodes_CharNextFunc unicodes_char_next */
- (PS_Macintosh_NameFunc) ps_get_macintosh_name, /* macintosh_name */
- (PS_Adobe_Std_StringsFunc) ps_get_standard_strings, /* adobe_std_strings */
+ ps_get_macintosh_name, /* PS_Macintosh_NameFunc macintosh_name */
+ ps_get_standard_strings, /* PS_Adobe_Std_StringsFunc adobe_std_strings */
t1_standard_encoding, /* adobe_std_encoding */
t1_expert_encoding /* adobe_expert_encoding */
@@ -560,13 +560,13 @@
FT_DEFINE_SERVICE_PSCMAPSREC(
pscmaps_interface,
- NULL, /* unicode_value */
- NULL, /* unicodes_init */
- NULL, /* unicodes_char_index */
- NULL, /* unicodes_char_next */
+ NULL, /* PS_Unicode_ValueFunc unicode_value */
+ NULL, /* PS_Unicodes_InitFunc unicodes_init */
+ NULL, /* PS_Unicodes_CharIndexFunc unicodes_char_index */
+ NULL, /* PS_Unicodes_CharNextFunc unicodes_char_next */
- (PS_Macintosh_NameFunc) ps_get_macintosh_name, /* macintosh_name */
- (PS_Adobe_Std_StringsFunc) ps_get_standard_strings, /* adobe_std_strings */
+ ps_get_macintosh_name, /* PS_Macintosh_NameFunc macintosh_name */
+ ps_get_standard_strings, /* PS_Adobe_Std_StringsFunc adobe_std_strings */
t1_standard_encoding, /* adobe_std_encoding */
t1_expert_encoding /* adobe_expert_encoding */
@@ -612,9 +612,9 @@
PUT_PS_NAMES_SERVICE(
(void*)&pscmaps_interface ), /* module specific interface */
- (FT_Module_Constructor)NULL, /* module_init */
- (FT_Module_Destructor) NULL, /* module_done */
- (FT_Module_Requester) PUT_PS_NAMES_SERVICE( psnames_get_service ) /* get_interface */
+ NULL, /* FT_Module_Constructor module_init */
+ NULL, /* FT_Module_Destructor module_done */
+ PUT_PS_NAMES_SERVICE( psnames_get_service ) /* FT_Module_Requester get_interface */
)
diff --git a/thirdparty/freetype/src/raster/ftraster.c b/thirdparty/freetype/src/raster/ftraster.c
index 67cbfd5d9b..192ca0701a 100644
--- a/thirdparty/freetype/src/raster/ftraster.c
+++ b/thirdparty/freetype/src/raster/ftraster.c
@@ -1742,9 +1742,9 @@
* SUCCESS on success, FAILURE on error.
*/
static Bool
- Decompose_Curve( RAS_ARGS UShort first,
- UShort last,
- Int flipped )
+ Decompose_Curve( RAS_ARGS Int first,
+ Int last,
+ Int flipped )
{
FT_Vector v_last;
FT_Vector v_control;
@@ -1969,8 +1969,8 @@
static Bool
Convert_Glyph( RAS_ARGS Int flipped )
{
- Int i;
- UInt start;
+ Int i;
+ Int first, last;
ras.fProfile = NULL;
@@ -1985,8 +1985,7 @@
ras.cProfile->offset = ras.top;
ras.num_Profs = 0;
- start = 0;
-
+ last = -1;
for ( i = 0; i < ras.outline.n_contours; i++ )
{
PProfile lastProfile;
@@ -1996,12 +1995,11 @@
ras.state = Unknown_State;
ras.gProfile = NULL;
- if ( Decompose_Curve( RAS_VARS (UShort)start,
- (UShort)ras.outline.contours[i],
- flipped ) )
- return FAILURE;
+ first = last + 1;
+ last = ras.outline.contours[i];
- start = (UShort)ras.outline.contours[i] + 1;
+ if ( Decompose_Curve( RAS_VARS first, last, flipped ) )
+ return FAILURE;
/* we must now check whether the extreme arcs join or not */
if ( FRAC( ras.lastY ) == 0 &&
@@ -3167,9 +3165,12 @@
static int
- ft_black_new( FT_Memory memory,
- black_PRaster *araster )
+ ft_black_new( void* memory_, /* FT_Memory */
+ FT_Raster *araster_ ) /* black_PRaster */
{
+ FT_Memory memory = (FT_Memory)memory_;
+ black_PRaster *araster = (black_PRaster*)araster_;
+
FT_Error error;
black_PRaster raster = NULL;
@@ -3184,9 +3185,10 @@
static void
- ft_black_done( black_PRaster raster )
+ ft_black_done( FT_Raster raster_ ) /* black_PRaster */
{
- FT_Memory memory = (FT_Memory)raster->memory;
+ black_PRaster raster = (black_PRaster)raster_;
+ FT_Memory memory = (FT_Memory)raster->memory;
FT_FREE( raster );
@@ -3281,11 +3283,11 @@
FT_GLYPH_FORMAT_OUTLINE,
- (FT_Raster_New_Func) ft_black_new, /* raster_new */
- (FT_Raster_Reset_Func) ft_black_reset, /* raster_reset */
- (FT_Raster_Set_Mode_Func)ft_black_set_mode, /* raster_set_mode */
- (FT_Raster_Render_Func) ft_black_render, /* raster_render */
- (FT_Raster_Done_Func) ft_black_done /* raster_done */
+ ft_black_new, /* FT_Raster_New_Func raster_new */
+ ft_black_reset, /* FT_Raster_Reset_Func raster_reset */
+ ft_black_set_mode, /* FT_Raster_Set_Mode_Func raster_set_mode */
+ ft_black_render, /* FT_Raster_Render_Func raster_render */
+ ft_black_done /* FT_Raster_Done_Func raster_done */
)
diff --git a/thirdparty/freetype/src/raster/ftrend1.c b/thirdparty/freetype/src/raster/ftrend1.c
index 0b5d867147..6d442b1ff8 100644
--- a/thirdparty/freetype/src/raster/ftrend1.c
+++ b/thirdparty/freetype/src/raster/ftrend1.c
@@ -27,8 +27,11 @@
/* initialize renderer -- init its raster */
static FT_Error
- ft_raster1_init( FT_Renderer render )
+ ft_raster1_init( FT_Module module ) /* FT_Renderer */
{
+ FT_Renderer render = (FT_Renderer)module;
+
+
render->clazz->raster_class->raster_reset( render->raster, NULL, 0 );
return FT_Err_Ok;
@@ -188,18 +191,18 @@
NULL, /* module specific interface */
- (FT_Module_Constructor)ft_raster1_init, /* module_init */
- (FT_Module_Destructor) NULL, /* module_done */
- (FT_Module_Requester) NULL, /* get_interface */
+ ft_raster1_init, /* FT_Module_Constructor module_init */
+ NULL, /* FT_Module_Destructor module_done */
+ NULL, /* FT_Module_Requester get_interface */
FT_GLYPH_FORMAT_OUTLINE,
- (FT_Renderer_RenderFunc) ft_raster1_render, /* render_glyph */
- (FT_Renderer_TransformFunc)ft_raster1_transform, /* transform_glyph */
- (FT_Renderer_GetCBoxFunc) ft_raster1_get_cbox, /* get_glyph_cbox */
- (FT_Renderer_SetModeFunc) ft_raster1_set_mode, /* set_mode */
+ ft_raster1_render, /* FT_Renderer_RenderFunc render_glyph */
+ ft_raster1_transform, /* FT_Renderer_TransformFunc transform_glyph */
+ ft_raster1_get_cbox, /* FT_Renderer_GetCBoxFunc get_glyph_cbox */
+ ft_raster1_set_mode, /* FT_Renderer_SetModeFunc set_mode */
- (FT_Raster_Funcs*)&ft_standard_raster /* raster_class */
+ &ft_standard_raster /* FT_Raster_Funcs* raster_class */
)
diff --git a/thirdparty/freetype/src/sdf/ftbsdf.c b/thirdparty/freetype/src/sdf/ftbsdf.c
index 901d8b7402..e472738339 100644
--- a/thirdparty/freetype/src/sdf/ftbsdf.c
+++ b/thirdparty/freetype/src/sdf/ftbsdf.c
@@ -1173,9 +1173,12 @@
/* called when adding a new module through @FT_Add_Module */
static FT_Error
- bsdf_raster_new( FT_Memory memory,
- BSDF_PRaster* araster )
+ bsdf_raster_new( void* memory_, /* FT_Memory */
+ FT_Raster* araster_ ) /* BSDF_PRaster* */
{
+ FT_Memory memory = (FT_Memory)memory_;
+ BSDF_PRaster* araster = (BSDF_PRaster*)araster_;
+
FT_Error error;
BSDF_PRaster raster = NULL;
diff --git a/thirdparty/freetype/src/sdf/ftsdf.c b/thirdparty/freetype/src/sdf/ftsdf.c
index 26a6d00e4a..bc4625d984 100644
--- a/thirdparty/freetype/src/sdf/ftsdf.c
+++ b/thirdparty/freetype/src/sdf/ftsdf.c
@@ -2371,11 +2371,11 @@
* ```
*
* (6) Our task is to find a value of `t` such that the above equation
- * `Q(t)` becomes zero, this is, the point-to-curve vector makes
+ * `Q(t)` becomes zero, that is, the point-to-curve vector makes
* 90~degrees with the curve. We solve this with the Newton-Raphson
* method.
*
- * (7) We first assume an arbitary value of factor `t`, which we then
+ * (7) We first assume an arbitrary value of factor `t`, which we then
* improve.
*
* ```
@@ -2684,11 +2684,11 @@
* ```
*
* (6) Our task is to find a value of `t` such that the above equation
- * `Q(t)` becomes zero, this is, the point-to-curve vector makes
+ * `Q(t)` becomes zero, that is, the point-to-curve vector makes
* 90~degree with curve. We solve this with the Newton-Raphson
* method.
*
- * (7) We first assume an arbitary value of factor `t`, which we then
+ * (7) We first assume an arbitrary value of factor `t`, which we then
* improve.
*
* ```
@@ -2718,8 +2718,9 @@
FT_Error error = FT_Err_Ok;
- FT_26D6_Vec aA, bB, cC, dD; /* A, B, C in the above comment */
- FT_16D16_Vec nearest_point; /* point on curve nearest to `point` */
+ FT_26D6_Vec aA, bB, cC, dD; /* A, B, C, D in the above comment */
+ FT_16D16_Vec nearest_point = { 0, 0 };
+ /* point on curve nearest to `point` */
FT_16D16_Vec direction; /* direction of curve at `nearest_point` */
FT_26D6_Vec p0, p1, p2, p3; /* control points of a cubic curve */
@@ -3761,9 +3762,13 @@
*/
static FT_Error
- sdf_raster_new( FT_Memory memory,
- SDF_PRaster* araster )
+ sdf_raster_new( void* memory_, /* FT_Memory */
+ FT_Raster* araster_ ) /* SDF_PRaster* */
{
+ FT_Memory memory = (FT_Memory)memory_;
+ SDF_PRaster* araster = (SDF_PRaster*)araster_;
+
+
FT_Error error;
SDF_PRaster raster = NULL;
diff --git a/thirdparty/freetype/src/sdf/ftsdfrend.c b/thirdparty/freetype/src/sdf/ftsdfrend.c
index 9ac7d6f620..5610c119f8 100644
--- a/thirdparty/freetype/src/sdf/ftsdfrend.c
+++ b/thirdparty/freetype/src/sdf/ftsdfrend.c
@@ -197,10 +197,10 @@
static FT_Module_Interface
- ft_sdf_requester( FT_Renderer render,
+ ft_sdf_requester( FT_Module module,
const char* module_interface )
{
- FT_UNUSED( render );
+ FT_UNUSED( module );
return ft_service_list_lookup( sdf_services, module_interface );
}
@@ -221,9 +221,9 @@
*/
static FT_Error
- ft_sdf_init( FT_Renderer render )
+ ft_sdf_init( FT_Module module ) /* SDF_Renderer */
{
- SDF_Renderer sdf_render = SDF_RENDERER( render );
+ SDF_Renderer sdf_render = SDF_RENDERER( module );
sdf_render->spread = DEFAULT_SPREAD;
@@ -236,9 +236,9 @@
static void
- ft_sdf_done( FT_Renderer render )
+ ft_sdf_done( FT_Module module )
{
- FT_UNUSED( render );
+ FT_UNUSED( module );
}
@@ -300,7 +300,7 @@
/* nothing to render */
if ( !bitmap->rows || !bitmap->pitch )
- return FT_Err_Ok;
+ goto Exit;
/* the padding will simply be equal to the `spread' */
x_pad = sdf_module->spread;
@@ -508,6 +508,10 @@
goto Exit;
}
+ /* nothing to render */
+ if ( !bitmap->rows || !bitmap->pitch )
+ goto Exit;
+
/* Do not generate SDF if the bitmap is not owned by the */
/* glyph: it might be that the source buffer is already freed. */
if ( !( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) )
@@ -519,10 +523,6 @@
goto Exit;
}
- /* nothing to render */
- if ( !bitmap->rows || !bitmap->pitch )
- return FT_Err_Ok;
-
FT_Bitmap_New( &target );
/* padding will simply be equal to `spread` */
@@ -557,15 +557,14 @@
{
/* the glyph is successfully converted to a SDF */
if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
- {
FT_FREE( bitmap->buffer );
- slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
- }
- slot->bitmap = target;
- slot->bitmap_top += y_pad;
- slot->bitmap_left -= x_pad;
- slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
+ slot->bitmap = target;
+ slot->bitmap_top += y_pad;
+ slot->bitmap_left -= x_pad;
+
+ if ( target.buffer )
+ slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
}
else if ( target.buffer )
FT_FREE( target.buffer );
diff --git a/thirdparty/freetype/src/sfnt/pngshim.c b/thirdparty/freetype/src/sfnt/pngshim.c
index 423b07b02a..33712162e0 100644
--- a/thirdparty/freetype/src/sfnt/pngshim.c
+++ b/thirdparty/freetype/src/sfnt/pngshim.c
@@ -406,10 +406,7 @@
switch ( color_type )
{
- default:
- /* Shouldn't happen, but ... */
- FALL_THROUGH;
-
+ default: /* Shouldn't happen, but ... */
case PNG_COLOR_TYPE_RGB_ALPHA:
png_set_read_user_transform_fn( png, premultiply_data );
break;
@@ -457,7 +454,7 @@
#else /* !(TT_CONFIG_OPTION_EMBEDDED_BITMAPS && FT_CONFIG_OPTION_USE_PNG) */
/* ANSI C doesn't like empty source files */
- typedef int _pngshim_dummy;
+ typedef int pngshim_dummy_;
#endif /* !(TT_CONFIG_OPTION_EMBEDDED_BITMAPS && FT_CONFIG_OPTION_USE_PNG) */
diff --git a/thirdparty/freetype/src/sfnt/sfdriver.c b/thirdparty/freetype/src/sfnt/sfdriver.c
index 762883db54..0925940b03 100644
--- a/thirdparty/freetype/src/sfnt/sfdriver.c
+++ b/thirdparty/freetype/src/sfnt/sfdriver.c
@@ -79,41 +79,57 @@
*
*/
- static void*
- get_sfnt_table( TT_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ sfnt_load_table( FT_Face face, /* TT_Face */
+ FT_ULong tag,
+ FT_Long offset,
+ FT_Byte* buffer,
+ FT_ULong* length )
+ {
+ TT_Face ttface = (TT_Face)face;
+
+
+ return tt_face_load_any( ttface, tag, offset, buffer, length );
+ }
+
+
+ FT_CALLBACK_DEF( void* )
+ get_sfnt_table( FT_Face face, /* TT_Face */
FT_Sfnt_Tag tag )
{
+ TT_Face ttface = (TT_Face)face;
+
void* table;
switch ( tag )
{
case FT_SFNT_HEAD:
- table = &face->header;
+ table = &ttface->header;
break;
case FT_SFNT_HHEA:
- table = &face->horizontal;
+ table = &ttface->horizontal;
break;
case FT_SFNT_VHEA:
- table = face->vertical_info ? &face->vertical : NULL;
+ table = ttface->vertical_info ? &ttface->vertical : NULL;
break;
case FT_SFNT_OS2:
- table = ( face->os2.version == 0xFFFFU ) ? NULL : &face->os2;
+ table = ( ttface->os2.version == 0xFFFFU ) ? NULL : &ttface->os2;
break;
case FT_SFNT_POST:
- table = &face->postscript;
+ table = &ttface->postscript;
break;
case FT_SFNT_MAXP:
- table = &face->max_profile;
+ table = &ttface->max_profile;
break;
case FT_SFNT_PCLT:
- table = face->pclt.Version ? &face->pclt : NULL;
+ table = ttface->pclt.Version ? &ttface->pclt : NULL;
break;
default:
@@ -124,26 +140,29 @@
}
- static FT_Error
- sfnt_table_info( TT_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ sfnt_table_info( FT_Face face, /* TT_Face */
FT_UInt idx,
FT_ULong *tag,
FT_ULong *offset,
FT_ULong *length )
{
+ TT_Face ttface = (TT_Face)face;
+
+
if ( !offset || !length )
return FT_THROW( Invalid_Argument );
if ( !tag )
- *length = face->num_tables;
+ *length = ttface->num_tables;
else
{
- if ( idx >= face->num_tables )
+ if ( idx >= ttface->num_tables )
return FT_THROW( Table_Missing );
- *tag = face->dir_tables[idx].Tag;
- *offset = face->dir_tables[idx].Offset;
- *length = face->dir_tables[idx].Length;
+ *tag = ttface->dir_tables[idx].Tag;
+ *offset = ttface->dir_tables[idx].Offset;
+ *length = ttface->dir_tables[idx].Length;
}
return FT_Err_Ok;
@@ -153,9 +172,9 @@
FT_DEFINE_SERVICE_SFNT_TABLEREC(
sfnt_service_sfnt_table,
- (FT_SFNT_TableLoadFunc)tt_face_load_any, /* load_table */
- (FT_SFNT_TableGetFunc) get_sfnt_table, /* get_table */
- (FT_SFNT_TableInfoFunc)sfnt_table_info /* table_info */
+ sfnt_load_table, /* FT_SFNT_TableLoadFunc load_table */
+ get_sfnt_table, /* FT_SFNT_TableGetFunc get_table */
+ sfnt_table_info /* FT_SFNT_TableInfoFunc table_info */
)
@@ -166,7 +185,7 @@
*
*/
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
sfnt_get_glyph_name( FT_Face face,
FT_UInt glyph_index,
FT_Pointer buffer,
@@ -184,7 +203,7 @@
}
- static FT_UInt
+ FT_CALLBACK_DEF( FT_UInt )
sfnt_get_name_index( FT_Face face,
const FT_String* glyph_name )
{
@@ -221,8 +240,8 @@
FT_DEFINE_SERVICE_GLYPHDICTREC(
sfnt_service_glyph_dict,
- (FT_GlyphDict_GetNameFunc) sfnt_get_glyph_name, /* get_name */
- (FT_GlyphDict_NameIndexFunc)sfnt_get_name_index /* name_index */
+ sfnt_get_glyph_name, /* FT_GlyphDict_GetNameFunc get_name */
+ sfnt_get_name_index /* FT_GlyphDict_NameIndexFunc name_index */
)
#endif /* TT_CONFIG_OPTION_POSTSCRIPT_NAMES */
@@ -523,15 +542,14 @@
FT_TRACE0(( "get_win_string:"
" Character 0x%X invalid in PS name string\n",
((unsigned)p[0])*256 + (unsigned)p[1] ));
- break;
+ continue;
}
}
- if ( !len )
- *r = '\0';
+ *r = '\0';
FT_FRAME_EXIT();
- if ( !len )
+ if ( r != result )
return result;
get_win_string_error:
@@ -580,15 +598,14 @@
FT_TRACE0(( "get_apple_string:"
" Character `%c' (0x%X) invalid in PS name string\n",
*p, *p ));
- break;
+ continue;
}
}
- if ( !len )
- *r = '\0';
+ *r = '\0';
FT_FRAME_EXIT();
- if ( !len )
+ if ( r != result )
return result;
get_apple_string_error:
@@ -602,7 +619,7 @@
}
- static FT_Bool
+ FT_CALLBACK_DEF( FT_Bool )
sfnt_get_name_id( TT_Face face,
FT_UShort id,
FT_Int *win,
@@ -819,9 +836,9 @@
if ( !found )
{
- /* as a last resort we try the family name; note that this is */
- /* not in the Adobe TechNote, but GX fonts (which predate the */
- /* TechNote) benefit from this behaviour */
+ /* according to the 'name' documentation in the OpenType */
+ /* specification the font family name is to be used if the */
+ /* typographic family name is missing, so let's do that */
found = sfnt_get_name_id( face,
TT_NAME_ID_FONT_FAMILY,
&win,
@@ -853,6 +870,10 @@
{
FT_TRACE0(( "sfnt_get_var_ps_name:"
" No valid PS name prefix for font instances found\n" ));
+ /* XXX It probably makes sense to never let this fail */
+ /* since an arbitrary prefix should work, too. */
+ /* On the other hand, it is very unlikely that */
+ /* we ever reach this code at all. */
return NULL;
}
@@ -1041,47 +1062,49 @@
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
- static const char*
- sfnt_get_ps_name( TT_Face face )
+ FT_CALLBACK_DEF( const char* )
+ sfnt_get_ps_name( FT_Face face ) /* TT_Face */
{
+ TT_Face ttface = (TT_Face)face;
+
FT_Int found, win, apple;
const char* result = NULL;
- if ( face->postscript_name )
- return face->postscript_name;
+ if ( ttface->postscript_name )
+ return ttface->postscript_name;
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
- if ( face->blend &&
- ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ||
- FT_IS_VARIATION( FT_FACE( face ) ) ) )
+ if ( ttface->blend &&
+ ( FT_IS_NAMED_INSTANCE( face ) ||
+ FT_IS_VARIATION( face ) ) )
{
- face->postscript_name = sfnt_get_var_ps_name( face );
- return face->postscript_name;
+ ttface->postscript_name = sfnt_get_var_ps_name( ttface );
+ return ttface->postscript_name;
}
#endif
/* scan the name table to see whether we have a Postscript name here, */
/* either in Macintosh or Windows platform encodings */
- found = sfnt_get_name_id( face, TT_NAME_ID_PS_NAME, &win, &apple );
+ found = sfnt_get_name_id( ttface, TT_NAME_ID_PS_NAME, &win, &apple );
if ( !found )
return NULL;
/* prefer Windows entries over Apple */
if ( win != -1 )
- result = get_win_string( face->root.memory,
- face->name_table.stream,
- face->name_table.names + win,
+ result = get_win_string( FT_FACE_MEMORY( face ),
+ ttface->name_table.stream,
+ ttface->name_table.names + win,
sfnt_is_postscript,
1 );
if ( !result && apple != -1 )
- result = get_apple_string( face->root.memory,
- face->name_table.stream,
- face->name_table.names + apple,
+ result = get_apple_string( FT_FACE_MEMORY( face ),
+ ttface->name_table.stream,
+ ttface->name_table.names + apple,
sfnt_is_postscript,
1 );
- face->postscript_name = result;
+ ttface->postscript_name = result;
return result;
}
@@ -1090,7 +1113,7 @@
FT_DEFINE_SERVICE_PSFONTNAMEREC(
sfnt_service_ps_name,
- (FT_PsName_GetFunc)sfnt_get_ps_name /* get_ps_font_name */
+ sfnt_get_ps_name /* FT_PsName_GetFunc get_ps_font_name */
)
@@ -1100,14 +1123,14 @@
FT_DEFINE_SERVICE_TTCMAPSREC(
tt_service_get_cmap_info,
- (TT_CMap_Info_GetFunc)tt_get_cmap_info /* get_cmap_info */
+ tt_get_cmap_info /* TT_CMap_Info_GetFunc get_cmap_info */
)
#ifdef TT_CONFIG_OPTION_BDF
static FT_Error
- sfnt_get_charset_id( TT_Face face,
+ sfnt_get_charset_id( FT_Face face,
const char* *acharset_encoding,
const char* *acharset_registry )
{
@@ -1145,8 +1168,8 @@
FT_DEFINE_SERVICE_BDFRec(
sfnt_service_bdf,
- (FT_BDF_GetCharsetIdFunc)sfnt_get_charset_id, /* get_charset_id */
- (FT_BDF_GetPropertyFunc) tt_face_find_bdf_prop /* get_property */
+ sfnt_get_charset_id, /* FT_BDF_GetCharsetIdFunc get_charset_id */
+ tt_face_find_bdf_prop /* FT_BDF_GetPropertyFunc get_property */
)
@@ -1337,9 +1360,9 @@
(const void*)&sfnt_interface, /* module specific interface */
- (FT_Module_Constructor)NULL, /* module_init */
- (FT_Module_Destructor) NULL, /* module_done */
- (FT_Module_Requester) sfnt_get_interface /* get_interface */
+ NULL, /* FT_Module_Constructor module_init */
+ NULL, /* FT_Module_Destructor module_done */
+ sfnt_get_interface /* FT_Module_Requester get_interface */
)
diff --git a/thirdparty/freetype/src/sfnt/sfobjs.c b/thirdparty/freetype/src/sfnt/sfobjs.c
index e018934cca..f5d66ef840 100644
--- a/thirdparty/freetype/src/sfnt/sfobjs.c
+++ b/thirdparty/freetype/src/sfnt/sfobjs.c
@@ -534,17 +534,23 @@
0 );
}
- if ( !face->var )
+ if ( !face->tt_var )
{
/* we want the metrics variations interface */
/* from the `truetype' module only */
FT_Module tt_module = FT_Get_Module( library, "truetype" );
- face->var = ft_module_get_service( tt_module,
- FT_SERVICE_ID_METRICS_VARIATIONS,
- 0 );
+ face->tt_var = ft_module_get_service( tt_module,
+ FT_SERVICE_ID_METRICS_VARIATIONS,
+ 0 );
}
+
+ if ( !face->face_var )
+ face->face_var = ft_module_get_service(
+ &face->root.driver->root,
+ FT_SERVICE_ID_METRICS_VARIATIONS,
+ 0 );
#endif
FT_TRACE2(( "SFNT driver\n" ));
@@ -692,6 +698,9 @@
instance_offset += instance_size;
}
+ /* named instance indices start with value 1 */
+ face->var_default_named_instance = i + 1;
+
if ( i == num_instances )
{
/* no default instance in named instance table; */
@@ -1054,6 +1063,16 @@
GET_NAME( FONT_SUBFAMILY, &face->root.style_name );
}
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ {
+ FT_Memory memory = face->root.memory;
+
+
+ if ( FT_STRDUP( face->non_var_style_name, face->root.style_name ) )
+ goto Exit;
+ }
+#endif
+
/* now set up root fields */
{
FT_Face root = &face->root;
@@ -1221,7 +1240,7 @@
if ( count > 0 )
{
- FT_Memory memory = face->root.stream->memory;
+ FT_Memory memory = face->root.memory;
FT_UShort em_size = face->header.Units_Per_EM;
FT_Short avgwidth = face->os2.xAvgCharWidth;
FT_Size_Metrics metrics;
@@ -1500,6 +1519,7 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
FT_FREE( face->var_postscript_prefix );
+ FT_FREE( face->non_var_style_name );
#endif
/* freeing glyph color palette data */
diff --git a/thirdparty/freetype/src/sfnt/sfwoff.c b/thirdparty/freetype/src/sfnt/sfwoff.c
index 9559bf3421..7c0ce2205e 100644
--- a/thirdparty/freetype/src/sfnt/sfwoff.c
+++ b/thirdparty/freetype/src/sfnt/sfwoff.c
@@ -426,7 +426,7 @@
#else /* !FT_CONFIG_OPTION_USE_ZLIB */
/* ANSI C doesn't like empty source files */
- typedef int _sfwoff_dummy;
+ typedef int sfwoff_dummy_;
#endif /* !FT_CONFIG_OPTION_USE_ZLIB */
diff --git a/thirdparty/freetype/src/sfnt/sfwoff2.c b/thirdparty/freetype/src/sfnt/sfwoff2.c
index 7a01977f86..49ad5cc811 100644
--- a/thirdparty/freetype/src/sfnt/sfwoff2.c
+++ b/thirdparty/freetype/src/sfnt/sfwoff2.c
@@ -2378,7 +2378,7 @@
#else /* !FT_CONFIG_OPTION_USE_BROTLI */
/* ANSI C doesn't like empty source files */
- typedef int _sfwoff2_dummy;
+ typedef int sfwoff2_dummy_;
#endif /* !FT_CONFIG_OPTION_USE_BROTLI */
diff --git a/thirdparty/freetype/src/sfnt/ttbdf.c b/thirdparty/freetype/src/sfnt/ttbdf.c
index 118f475e7f..536fa7467e 100644
--- a/thirdparty/freetype/src/sfnt/ttbdf.c
+++ b/thirdparty/freetype/src/sfnt/ttbdf.c
@@ -136,13 +136,14 @@
FT_LOCAL_DEF( FT_Error )
- tt_face_find_bdf_prop( TT_Face face,
+ tt_face_find_bdf_prop( FT_Face face, /* TT_Face */
const char* property_name,
BDF_PropertyRec *aprop )
{
- TT_BDF bdf = &face->bdf;
- FT_Size size = FT_FACE( face )->size;
- FT_Error error = FT_Err_Ok;
+ TT_Face ttface = (TT_Face)face;
+ TT_BDF bdf = &ttface->bdf;
+ FT_Size size = FT_FACE_SIZE( face );
+ FT_Error error = FT_Err_Ok;
FT_Byte* p;
FT_UInt count;
FT_Byte* strike;
@@ -153,7 +154,7 @@
if ( bdf->loaded == 0 )
{
- error = tt_face_load_bdf_props( face, FT_FACE( face )->stream );
+ error = tt_face_load_bdf_props( ttface, FT_FACE_STREAM( face ) );
if ( error )
goto Exit;
}
@@ -248,7 +249,7 @@
#else /* !TT_CONFIG_OPTION_BDF */
/* ANSI C doesn't like empty source files */
- typedef int _tt_bdf_dummy;
+ typedef int tt_bdf_dummy_;
#endif /* !TT_CONFIG_OPTION_BDF */
diff --git a/thirdparty/freetype/src/sfnt/ttbdf.h b/thirdparty/freetype/src/sfnt/ttbdf.h
index 595aeb76c2..0d7a0acecc 100644
--- a/thirdparty/freetype/src/sfnt/ttbdf.h
+++ b/thirdparty/freetype/src/sfnt/ttbdf.h
@@ -34,7 +34,7 @@ FT_BEGIN_HEADER
FT_LOCAL( FT_Error )
- tt_face_find_bdf_prop( TT_Face face,
+ tt_face_find_bdf_prop( FT_Face face,
const char* property_name,
BDF_PropertyRec *aprop );
diff --git a/thirdparty/freetype/src/sfnt/ttcmap.c b/thirdparty/freetype/src/sfnt/ttcmap.c
index 820cd08e6d..9ba25dcbc1 100644
--- a/thirdparty/freetype/src/sfnt/ttcmap.c
+++ b/thirdparty/freetype/src/sfnt/ttcmap.c
@@ -59,10 +59,14 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap_init( TT_CMap cmap,
- FT_Byte* table )
+ tt_cmap_init( FT_CMap cmap, /* TT_CMap */
+ void* table_ )
{
- cmap->data = table;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* table = (FT_Byte*)table_;
+
+
+ ttcmap->data = table;
return FT_Err_Ok;
}
@@ -128,21 +132,23 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap0_char_index( TT_CMap cmap,
+ tt_cmap0_char_index( FT_CMap cmap, /* TT_CMap */
FT_UInt32 char_code )
{
- FT_Byte* table = cmap->data;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* table = ttcmap->data;
return char_code < 256 ? table[6 + char_code] : 0;
}
- FT_CALLBACK_DEF( FT_UInt32 )
- tt_cmap0_char_next( TT_CMap cmap,
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap0_char_next( FT_CMap cmap, /* TT_CMap */
FT_UInt32 *pchar_code )
{
- FT_Byte* table = cmap->data;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* table = ttcmap->data;
FT_UInt32 charcode = *pchar_code;
FT_UInt32 result = 0;
FT_UInt gindex = 0;
@@ -165,10 +171,11 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap0_get_info( TT_CMap cmap,
+ tt_cmap0_get_info( FT_CharMap cmap, /* TT_CMap */
TT_CMapInfo *cmap_info )
{
- FT_Byte* p = cmap->data + 4;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* p = ttcmap->data + 4;
cmap_info->format = 0;
@@ -453,10 +460,11 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap2_char_index( TT_CMap cmap,
+ tt_cmap2_char_index( FT_CMap cmap, /* TT_CMap */
FT_UInt32 char_code )
{
- FT_Byte* table = cmap->data;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* table = ttcmap->data;
FT_UInt result = 0;
FT_Byte* subheader;
@@ -491,11 +499,12 @@
}
- FT_CALLBACK_DEF( FT_UInt32 )
- tt_cmap2_char_next( TT_CMap cmap,
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap2_char_next( FT_CMap cmap, /* TT_CMap */
FT_UInt32 *pcharcode )
{
- FT_Byte* table = cmap->data;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* table = ttcmap->data;
FT_UInt gindex = 0;
FT_UInt32 result = 0;
FT_UInt32 charcode = *pcharcode + 1;
@@ -579,10 +588,11 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap2_get_info( TT_CMap cmap,
+ tt_cmap2_get_info( FT_CharMap cmap, /* TT_CMap */
TT_CMapInfo *cmap_info )
{
- FT_Byte* p = cmap->data + 4;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* p = ttcmap->data + 4;
cmap_info->format = 2;
@@ -706,18 +716,20 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap4_init( TT_CMap4 cmap,
- FT_Byte* table )
+ tt_cmap4_init( FT_CMap cmap, /* TT_CMap4 */
+ void* table_ )
{
+ TT_CMap4 ttcmap = (TT_CMap4)cmap;
+ FT_Byte* table = (FT_Byte*)table_;
FT_Byte* p;
- cmap->cmap.data = table;
+ ttcmap->cmap.data = table;
- p = table + 6;
- cmap->num_ranges = FT_PEEK_USHORT( p ) >> 1;
- cmap->cur_charcode = (FT_UInt32)0xFFFFFFFFUL;
- cmap->cur_gindex = 0;
+ p = table + 6;
+ ttcmap->num_ranges = FT_PEEK_USHORT( p ) >> 1;
+ ttcmap->cur_charcode = (FT_UInt32)0xFFFFFFFFUL;
+ ttcmap->cur_gindex = 0;
return FT_Err_Ok;
}
@@ -755,7 +767,7 @@
cmap->cur_start == 0xFFFFU &&
cmap->cur_end == 0xFFFFU )
{
- TT_Face face = (TT_Face)cmap->cmap.cmap.charmap.face;
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
FT_Byte* limit = face->cmap_table + face->cmap_size;
@@ -788,15 +800,12 @@
static void
tt_cmap4_next( TT_CMap4 cmap )
{
- TT_Face face = (TT_Face)cmap->cmap.cmap.charmap.face;
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
FT_Byte* limit = face->cmap_table + face->cmap_size;
FT_UInt charcode;
- if ( cmap->cur_charcode >= 0xFFFFUL )
- goto Fail;
-
charcode = (FT_UInt)cmap->cur_charcode + 1;
if ( charcode < cmap->cur_start )
@@ -882,7 +891,6 @@
charcode = cmap->cur_start;
}
- Fail:
cmap->cur_charcode = (FT_UInt32)0xFFFFFFFFUL;
cmap->cur_gindex = 0;
}
@@ -1097,32 +1105,26 @@
FT_UInt32* pcharcode,
FT_Bool next )
{
- TT_Face face = (TT_Face)cmap->cmap.charmap.face;
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
FT_Byte* limit = face->cmap_table + face->cmap_size;
FT_UInt num_segs2, start, end, offset;
FT_Int delta;
FT_UInt i, num_segs;
- FT_UInt32 charcode = *pcharcode;
+ FT_UInt32 charcode = *pcharcode + next;
FT_UInt gindex = 0;
FT_Byte* p;
FT_Byte* q;
p = cmap->data + 6;
- num_segs2 = FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 2 );
-
- num_segs = num_segs2 >> 1;
+ num_segs = TT_PEEK_USHORT( p ) >> 1;
if ( !num_segs )
return 0;
- if ( next )
- charcode++;
-
- if ( charcode > 0xFFFFU )
- return 0;
+ num_segs2 = num_segs << 1;
/* linear search */
p = cmap->data + 14; /* ends table */
@@ -1232,37 +1234,30 @@
FT_UInt32* pcharcode,
FT_Bool next )
{
- TT_Face face = (TT_Face)cmap->cmap.charmap.face;
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
FT_Byte* limit = face->cmap_table + face->cmap_size;
FT_UInt num_segs2, start, end, offset;
FT_Int delta;
FT_UInt max, min, mid, num_segs;
- FT_UInt charcode = (FT_UInt)*pcharcode;
+ FT_UInt charcode = (FT_UInt)*pcharcode + next;
FT_UInt gindex = 0;
FT_Byte* p;
p = cmap->data + 6;
- num_segs2 = FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 2 );
+ num_segs = TT_PEEK_USHORT( p ) >> 1;
- if ( !num_segs2 )
+ if ( !num_segs )
return 0;
- num_segs = num_segs2 >> 1;
-
- /* make compiler happy */
- mid = num_segs;
- end = 0xFFFFU;
-
- if ( next )
- charcode++;
+ num_segs2 = num_segs << 1;
min = 0;
max = num_segs;
/* binary search */
- while ( min < max )
+ do
{
mid = ( min + max ) >> 1;
p = cmap->data + 14 + mid * 2;
@@ -1445,6 +1440,7 @@
break;
}
}
+ while ( min < max );
if ( next )
{
@@ -1454,12 +1450,8 @@
/* if `charcode' is not in any segment, then `mid' is */
/* the segment nearest to `charcode' */
- if ( charcode > end )
- {
- mid++;
- if ( mid == num_segs )
- return 0;
- }
+ if ( charcode > end && ++mid == num_segs )
+ return 0;
if ( tt_cmap4_set_range( cmap4, mid ) )
{
@@ -1474,7 +1466,6 @@
cmap4->cur_gindex = gindex;
else
{
- cmap4->cur_charcode = charcode;
tt_cmap4_next( cmap4 );
gindex = cmap4->cur_gindex;
}
@@ -1489,31 +1480,35 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap4_char_index( TT_CMap cmap,
+ tt_cmap4_char_index( FT_CMap cmap, /* TT_CMap */
FT_UInt32 char_code )
{
+ TT_CMap ttcmap = (TT_CMap)cmap;
+
+
if ( char_code >= 0x10000UL )
return 0;
- if ( cmap->flags & TT_CMAP_FLAG_UNSORTED )
- return tt_cmap4_char_map_linear( cmap, &char_code, 0 );
+ if ( ttcmap->flags & TT_CMAP_FLAG_UNSORTED )
+ return tt_cmap4_char_map_linear( ttcmap, &char_code, 0 );
else
- return tt_cmap4_char_map_binary( cmap, &char_code, 0 );
+ return tt_cmap4_char_map_binary( ttcmap, &char_code, 0 );
}
- FT_CALLBACK_DEF( FT_UInt32 )
- tt_cmap4_char_next( TT_CMap cmap,
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap4_char_next( FT_CMap cmap, /* TT_CMap */
FT_UInt32 *pchar_code )
{
+ TT_CMap ttcmap = (TT_CMap)cmap;
FT_UInt gindex;
if ( *pchar_code >= 0xFFFFU )
return 0;
- if ( cmap->flags & TT_CMAP_FLAG_UNSORTED )
- gindex = tt_cmap4_char_map_linear( cmap, pchar_code, 1 );
+ if ( ttcmap->flags & TT_CMAP_FLAG_UNSORTED )
+ gindex = tt_cmap4_char_map_linear( ttcmap, pchar_code, 1 );
else
{
TT_CMap4 cmap4 = (TT_CMap4)cmap;
@@ -1528,7 +1523,7 @@
*pchar_code = cmap4->cur_charcode;
}
else
- gindex = tt_cmap4_char_map_binary( cmap, pchar_code, 1 );
+ gindex = tt_cmap4_char_map_binary( ttcmap, pchar_code, 1 );
}
return gindex;
@@ -1536,10 +1531,11 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap4_get_info( TT_CMap cmap,
+ tt_cmap4_get_info( FT_CharMap cmap, /* TT_CMap */
TT_CMapInfo *cmap_info )
{
- FT_Byte* p = cmap->data + 4;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* p = ttcmap->data + 4;
cmap_info->format = 4;
@@ -1640,10 +1636,11 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap6_char_index( TT_CMap cmap,
+ tt_cmap6_char_index( FT_CMap cmap, /* TT_CMap */
FT_UInt32 char_code )
{
- FT_Byte* table = cmap->data;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* table = ttcmap->data;
FT_UInt result = 0;
FT_Byte* p = table + 6;
FT_UInt start = TT_NEXT_USHORT( p );
@@ -1661,11 +1658,12 @@
}
- FT_CALLBACK_DEF( FT_UInt32 )
- tt_cmap6_char_next( TT_CMap cmap,
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap6_char_next( FT_CMap cmap, /* TT_CMap */
FT_UInt32 *pchar_code )
{
- FT_Byte* table = cmap->data;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* table = ttcmap->data;
FT_UInt32 result = 0;
FT_UInt32 char_code = *pchar_code + 1;
FT_UInt gindex = 0;
@@ -1706,10 +1704,11 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap6_get_info( TT_CMap cmap,
+ tt_cmap6_get_info( FT_CharMap cmap, /* TT_CMap */
TT_CMapInfo *cmap_info )
{
- FT_Byte* p = cmap->data + 4;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* p = ttcmap->data + 4;
cmap_info->format = 6;
@@ -1900,10 +1899,11 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap8_char_index( TT_CMap cmap,
+ tt_cmap8_char_index( FT_CMap cmap, /* TT_CMap */
FT_UInt32 char_code )
{
- FT_Byte* table = cmap->data;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* table = ttcmap->data;
FT_UInt result = 0;
FT_Byte* p = table + 8204;
FT_UInt32 num_groups = TT_NEXT_ULONG( p );
@@ -1932,15 +1932,16 @@
}
- FT_CALLBACK_DEF( FT_UInt32 )
- tt_cmap8_char_next( TT_CMap cmap,
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap8_char_next( FT_CMap cmap, /* TT_CMap */
FT_UInt32 *pchar_code )
{
- FT_Face face = cmap->cmap.charmap.face;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Face face = FT_CMAP_FACE( cmap );
FT_UInt32 result = 0;
FT_UInt32 char_code;
FT_UInt gindex = 0;
- FT_Byte* table = cmap->data;
+ FT_Byte* table = ttcmap->data;
FT_Byte* p = table + 8204;
FT_UInt32 num_groups = TT_NEXT_ULONG( p );
FT_UInt32 start, end, start_id;
@@ -2000,10 +2001,11 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap8_get_info( TT_CMap cmap,
+ tt_cmap8_get_info( FT_CharMap cmap, /* TT_CMap */
TT_CMapInfo *cmap_info )
{
- FT_Byte* p = cmap->data + 8;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* p = ttcmap->data + 8;
cmap_info->format = 8;
@@ -2104,10 +2106,11 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap10_char_index( TT_CMap cmap,
+ tt_cmap10_char_index( FT_CMap cmap, /* TT_CMap */
FT_UInt32 char_code )
{
- FT_Byte* table = cmap->data;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* table = ttcmap->data;
FT_UInt result = 0;
FT_Byte* p = table + 12;
FT_UInt32 start = TT_NEXT_ULONG( p );
@@ -2130,11 +2133,12 @@
}
- FT_CALLBACK_DEF( FT_UInt32 )
- tt_cmap10_char_next( TT_CMap cmap,
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap10_char_next( FT_CMap cmap, /* TT_CMap */
FT_UInt32 *pchar_code )
{
- FT_Byte* table = cmap->data;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* table = ttcmap->data;
FT_UInt32 char_code;
FT_UInt gindex = 0;
FT_Byte* p = table + 12;
@@ -2172,10 +2176,11 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap10_get_info( TT_CMap cmap,
+ tt_cmap10_get_info( FT_CharMap cmap, /* TT_CMap */
TT_CMapInfo *cmap_info )
{
- FT_Byte* p = cmap->data + 8;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* p = ttcmap->data + 8;
cmap_info->format = 10;
@@ -2253,15 +2258,19 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap12_init( TT_CMap12 cmap,
- FT_Byte* table )
+ tt_cmap12_init( FT_CMap cmap, /* TT_CMap12 */
+ void* table_ )
{
- cmap->cmap.data = table;
+ TT_CMap12 ttcmap = (TT_CMap12)cmap;
+ FT_Byte* table = (FT_Byte*)table_;
+
- table += 12;
- cmap->num_groups = FT_PEEK_ULONG( table );
+ ttcmap->cmap.data = table;
- cmap->valid = 0;
+ table += 12;
+ ttcmap->num_groups = FT_PEEK_ULONG( table );
+
+ ttcmap->valid = 0;
return FT_Err_Ok;
}
@@ -2331,23 +2340,21 @@
/* cmap->cur_group should be set up properly by caller */
/* */
static void
- tt_cmap12_next( TT_CMap12 cmap )
+ tt_cmap12_next( FT_CMap cmap ) /* TT_CMap12 */
{
- FT_Face face = cmap->cmap.cmap.charmap.face;
- FT_Byte* p;
- FT_ULong start, end, start_id, char_code;
- FT_ULong n;
- FT_UInt gindex;
-
+ TT_CMap12 ttcmap = (TT_CMap12)cmap;
+ FT_Face face = FT_CMAP_FACE( cmap );
+ FT_Byte* p;
+ FT_ULong start, end, start_id, char_code;
+ FT_ULong n;
+ FT_UInt gindex;
- if ( cmap->cur_charcode >= 0xFFFFFFFFUL )
- goto Fail;
- char_code = cmap->cur_charcode + 1;
+ char_code = ttcmap->cur_charcode + 1;
- for ( n = cmap->cur_group; n < cmap->num_groups; n++ )
+ for ( n = ttcmap->cur_group; n < ttcmap->num_groups; n++ )
{
- p = cmap->cmap.data + 16 + 12 * n;
+ p = ttcmap->cmap.data + 16 + 12 * n;
start = TT_NEXT_ULONG( p );
end = TT_NEXT_ULONG( p );
start_id = TT_PEEK_ULONG( p );
@@ -2379,16 +2386,16 @@
if ( gindex >= (FT_UInt)face->num_glyphs )
continue;
- cmap->cur_charcode = char_code;
- cmap->cur_gindex = gindex;
- cmap->cur_group = n;
+ ttcmap->cur_charcode = char_code;
+ ttcmap->cur_gindex = gindex;
+ ttcmap->cur_group = n;
return;
}
}
Fail:
- cmap->valid = 0;
+ ttcmap->valid = 0;
}
@@ -2400,7 +2407,7 @@
FT_UInt gindex = 0;
FT_Byte* p = cmap->data + 12;
FT_UInt32 num_groups = TT_PEEK_ULONG( p );
- FT_UInt32 char_code = *pchar_code;
+ FT_UInt32 char_code = *pchar_code + next;
FT_UInt32 start, end, start_id;
FT_UInt32 max, min, mid;
@@ -2408,23 +2415,11 @@
if ( !num_groups )
return 0;
- /* make compiler happy */
- mid = num_groups;
- end = 0xFFFFFFFFUL;
-
- if ( next )
- {
- if ( char_code >= 0xFFFFFFFFUL )
- return 0;
-
- char_code++;
- }
-
min = 0;
max = num_groups;
/* binary search */
- while ( min < max )
+ do
{
mid = ( min + max ) >> 1;
p = cmap->data + 16 + 12 * mid;
@@ -2448,22 +2443,19 @@
break;
}
}
+ while ( min < max );
if ( next )
{
- FT_Face face = cmap->cmap.charmap.face;
+ FT_Face face = FT_CMAP_FACE( cmap );
TT_CMap12 cmap12 = (TT_CMap12)cmap;
/* if `char_code' is not in any group, then `mid' is */
/* the group nearest to `char_code' */
- if ( char_code > end )
- {
- mid++;
- if ( mid == num_groups )
- return 0;
- }
+ if ( char_code > end && ++mid == num_groups )
+ return 0;
cmap12->valid = 1;
cmap12->cur_charcode = char_code;
@@ -2474,7 +2466,7 @@
if ( !gindex )
{
- tt_cmap12_next( cmap12 );
+ tt_cmap12_next( FT_CMAP( cmap12 ) );
if ( cmap12->valid )
gindex = cmap12->cur_gindex;
@@ -2490,25 +2482,28 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap12_char_index( TT_CMap cmap,
+ tt_cmap12_char_index( FT_CMap cmap, /* TT_CMap */
FT_UInt32 char_code )
{
- return tt_cmap12_char_map_binary( cmap, &char_code, 0 );
+ return tt_cmap12_char_map_binary( (TT_CMap)cmap, &char_code, 0 );
}
- FT_CALLBACK_DEF( FT_UInt32 )
- tt_cmap12_char_next( TT_CMap cmap,
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap12_char_next( FT_CMap cmap, /* TT_CMap12 */
FT_UInt32 *pchar_code )
{
TT_CMap12 cmap12 = (TT_CMap12)cmap;
FT_UInt gindex;
+ if ( *pchar_code >= 0xFFFFFFFFUL )
+ return 0;
+
/* no need to search */
if ( cmap12->valid && cmap12->cur_charcode == *pchar_code )
{
- tt_cmap12_next( cmap12 );
+ tt_cmap12_next( FT_CMAP( cmap12 ) );
if ( cmap12->valid )
{
gindex = cmap12->cur_gindex;
@@ -2518,17 +2513,18 @@
gindex = 0;
}
else
- gindex = tt_cmap12_char_map_binary( cmap, pchar_code, 1 );
+ gindex = tt_cmap12_char_map_binary( (TT_CMap)cmap, pchar_code, 1 );
return gindex;
}
FT_CALLBACK_DEF( FT_Error )
- tt_cmap12_get_info( TT_CMap cmap,
+ tt_cmap12_get_info( FT_CharMap cmap, /* TT_CMap */
TT_CMapInfo *cmap_info )
{
- FT_Byte* p = cmap->data + 8;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* p = ttcmap->data + 8;
cmap_info->format = 12;
@@ -2606,15 +2602,19 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap13_init( TT_CMap13 cmap,
- FT_Byte* table )
+ tt_cmap13_init( FT_CMap cmap, /* TT_CMap13 */
+ void* table_ )
{
- cmap->cmap.data = table;
+ TT_CMap13 ttcmap = (TT_CMap13)cmap;
+ FT_Byte* table = (FT_Byte*)table_;
+
+
+ ttcmap->cmap.data = table;
- table += 12;
- cmap->num_groups = FT_PEEK_ULONG( table );
+ table += 12;
+ ttcmap->num_groups = FT_PEEK_ULONG( table );
- cmap->valid = 0;
+ ttcmap->valid = 0;
return FT_Err_Ok;
}
@@ -2679,23 +2679,21 @@
/* cmap->cur_group should be set up properly by caller */
/* */
static void
- tt_cmap13_next( TT_CMap13 cmap )
+ tt_cmap13_next( FT_CMap cmap ) /* TT_CMap13 */
{
- FT_Face face = cmap->cmap.cmap.charmap.face;
- FT_Byte* p;
- FT_ULong start, end, glyph_id, char_code;
- FT_ULong n;
- FT_UInt gindex;
-
+ TT_CMap13 ttcmap = (TT_CMap13)cmap;
+ FT_Face face = FT_CMAP_FACE( cmap );
+ FT_Byte* p;
+ FT_ULong start, end, glyph_id, char_code;
+ FT_ULong n;
+ FT_UInt gindex;
- if ( cmap->cur_charcode >= 0xFFFFFFFFUL )
- goto Fail;
- char_code = cmap->cur_charcode + 1;
+ char_code = ttcmap->cur_charcode + 1;
- for ( n = cmap->cur_group; n < cmap->num_groups; n++ )
+ for ( n = ttcmap->cur_group; n < ttcmap->num_groups; n++ )
{
- p = cmap->cmap.data + 16 + 12 * n;
+ p = ttcmap->cmap.data + 16 + 12 * n;
start = TT_NEXT_ULONG( p );
end = TT_NEXT_ULONG( p );
glyph_id = TT_PEEK_ULONG( p );
@@ -2709,17 +2707,16 @@
if ( gindex && gindex < (FT_UInt)face->num_glyphs )
{
- cmap->cur_charcode = char_code;
- cmap->cur_gindex = gindex;
- cmap->cur_group = n;
+ ttcmap->cur_charcode = char_code;
+ ttcmap->cur_gindex = gindex;
+ ttcmap->cur_group = n;
return;
}
}
}
- Fail:
- cmap->valid = 0;
+ ttcmap->valid = 0;
}
@@ -2731,7 +2728,7 @@
FT_UInt gindex = 0;
FT_Byte* p = cmap->data + 12;
FT_UInt32 num_groups = TT_PEEK_ULONG( p );
- FT_UInt32 char_code = *pchar_code;
+ FT_UInt32 char_code = *pchar_code + next;
FT_UInt32 start, end;
FT_UInt32 max, min, mid;
@@ -2739,23 +2736,11 @@
if ( !num_groups )
return 0;
- /* make compiler happy */
- mid = num_groups;
- end = 0xFFFFFFFFUL;
-
- if ( next )
- {
- if ( char_code >= 0xFFFFFFFFUL )
- return 0;
-
- char_code++;
- }
-
min = 0;
max = num_groups;
/* binary search */
- while ( min < max )
+ do
{
mid = ( min + max ) >> 1;
p = cmap->data + 16 + 12 * mid;
@@ -2774,6 +2759,7 @@
break;
}
}
+ while ( min < max );
if ( next )
{
@@ -2784,12 +2770,8 @@
/* if `char_code' is not in any group, then `mid' is */
/* the group nearest to `char_code' */
- if ( char_code > end )
- {
- mid++;
- if ( mid == num_groups )
- return 0;
- }
+ if ( char_code > end && ++mid == num_groups )
+ return 0;
cmap13->valid = 1;
cmap13->cur_charcode = char_code;
@@ -2800,7 +2782,7 @@
if ( !gindex )
{
- tt_cmap13_next( cmap13 );
+ tt_cmap13_next( FT_CMAP( cmap13 ) );
if ( cmap13->valid )
gindex = cmap13->cur_gindex;
@@ -2816,25 +2798,28 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap13_char_index( TT_CMap cmap,
+ tt_cmap13_char_index( FT_CMap cmap, /* TT_CMap */
FT_UInt32 char_code )
{
- return tt_cmap13_char_map_binary( cmap, &char_code, 0 );
+ return tt_cmap13_char_map_binary( (TT_CMap)cmap, &char_code, 0 );
}
- FT_CALLBACK_DEF( FT_UInt32 )
- tt_cmap13_char_next( TT_CMap cmap,
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap13_char_next( FT_CMap cmap, /* TT_CMap13 */
FT_UInt32 *pchar_code )
{
TT_CMap13 cmap13 = (TT_CMap13)cmap;
FT_UInt gindex;
+ if ( *pchar_code >= 0xFFFFFFFFUL )
+ return 0;
+
/* no need to search */
if ( cmap13->valid && cmap13->cur_charcode == *pchar_code )
{
- tt_cmap13_next( cmap13 );
+ tt_cmap13_next( FT_CMAP( cmap13 ) );
if ( cmap13->valid )
{
gindex = cmap13->cur_gindex;
@@ -2844,17 +2829,18 @@
gindex = 0;
}
else
- gindex = tt_cmap13_char_map_binary( cmap, pchar_code, 1 );
+ gindex = tt_cmap13_char_map_binary( (TT_CMap)cmap, pchar_code, 1 );
return gindex;
}
FT_CALLBACK_DEF( FT_Error )
- tt_cmap13_get_info( TT_CMap cmap,
+ tt_cmap13_get_info( FT_CharMap cmap, /* TT_CMap */
TT_CMapInfo *cmap_info )
{
- FT_Byte* p = cmap->data + 8;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* p = ttcmap->data + 8;
cmap_info->format = 13;
@@ -2969,14 +2955,15 @@
FT_CALLBACK_DEF( void )
- tt_cmap14_done( TT_CMap14 cmap )
+ tt_cmap14_done( FT_CMap cmap ) /* TT_CMap14 */
{
- FT_Memory memory = cmap->memory;
+ TT_CMap14 ttcmap = (TT_CMap14)cmap;
+ FT_Memory memory = ttcmap->memory;
- cmap->max_results = 0;
- if ( memory && cmap->results )
- FT_FREE( cmap->results );
+ ttcmap->max_results = 0;
+ if ( memory && ttcmap->results )
+ FT_FREE( ttcmap->results );
}
@@ -3004,15 +2991,19 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap14_init( TT_CMap14 cmap,
- FT_Byte* table )
+ tt_cmap14_init( FT_CMap cmap, /* TT_CMap14 */
+ void* table_ )
{
- cmap->cmap.data = table;
+ TT_CMap14 ttcmap = (TT_CMap14)cmap;
+ FT_Byte* table = (FT_Byte*)table_;
+
- table += 6;
- cmap->num_selectors = FT_PEEK_ULONG( table );
- cmap->max_results = 0;
- cmap->results = NULL;
+ ttcmap->cmap.data = table;
+
+ table += 6;
+ ttcmap->num_selectors = FT_PEEK_ULONG( table );
+ ttcmap->max_results = 0;
+ ttcmap->results = NULL;
return FT_Err_Ok;
}
@@ -3142,7 +3133,7 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap14_char_index( TT_CMap cmap,
+ tt_cmap14_char_index( FT_CMap cmap,
FT_UInt32 char_code )
{
FT_UNUSED( cmap );
@@ -3153,8 +3144,8 @@
}
- FT_CALLBACK_DEF( FT_UInt32 )
- tt_cmap14_char_next( TT_CMap cmap,
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap14_char_next( FT_CMap cmap,
FT_UInt32 *pchar_code )
{
FT_UNUSED( cmap );
@@ -3166,7 +3157,7 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap14_get_info( TT_CMap cmap,
+ tt_cmap14_get_info( FT_CharMap cmap,
TT_CMapInfo *cmap_info )
{
FT_UNUSED( cmap );
@@ -3280,12 +3271,16 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap14_char_var_index( TT_CMap cmap,
- TT_CMap ucmap,
+ tt_cmap14_char_var_index( FT_CMap cmap, /* TT_CMap */
+ FT_CMap ucmap, /* TT_CMap */
FT_UInt32 charcode,
FT_UInt32 variantSelector )
{
- FT_Byte* p = tt_cmap14_find_variant( cmap->data + 6, variantSelector );
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ TT_CMap ttucmap = (TT_CMap)ucmap;
+
+ FT_Byte* p = tt_cmap14_find_variant( ttcmap->data + 6,
+ variantSelector );
FT_ULong defOff;
FT_ULong nondefOff;
@@ -3296,16 +3291,16 @@
defOff = TT_NEXT_ULONG( p );
nondefOff = TT_PEEK_ULONG( p );
- if ( defOff != 0 &&
- tt_cmap14_char_map_def_binary( cmap->data + defOff, charcode ) )
+ if ( defOff != 0 &&
+ tt_cmap14_char_map_def_binary( ttcmap->data + defOff, charcode ) )
{
/* This is the default variant of this charcode. GID not stored */
/* here; stored in the normal Unicode charmap instead. */
- return ucmap->cmap.clazz->char_index( &ucmap->cmap, charcode );
+ return ttucmap->cmap.clazz->char_index( &ttucmap->cmap, charcode );
}
if ( nondefOff != 0 )
- return tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff,
+ return tt_cmap14_char_map_nondef_binary( ttcmap->data + nondefOff,
charcode );
return 0;
@@ -3313,11 +3308,13 @@
FT_CALLBACK_DEF( FT_Int )
- tt_cmap14_char_var_isdefault( TT_CMap cmap,
+ tt_cmap14_char_var_isdefault( FT_CMap cmap, /* TT_CMap */
FT_UInt32 charcode,
FT_UInt32 variantSelector )
{
- FT_Byte* p = tt_cmap14_find_variant( cmap->data + 6, variantSelector );
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte* p = tt_cmap14_find_variant( ttcmap->data + 6,
+ variantSelector );
FT_ULong defOff;
FT_ULong nondefOff;
@@ -3328,13 +3325,13 @@
defOff = TT_NEXT_ULONG( p );
nondefOff = TT_NEXT_ULONG( p );
- if ( defOff != 0 &&
- tt_cmap14_char_map_def_binary( cmap->data + defOff, charcode ) )
+ if ( defOff != 0 &&
+ tt_cmap14_char_map_def_binary( ttcmap->data + defOff, charcode ) )
return 1;
- if ( nondefOff != 0 &&
- tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff,
- charcode ) != 0 )
+ if ( nondefOff != 0 &&
+ tt_cmap14_char_map_nondef_binary( ttcmap->data + nondefOff,
+ charcode ) != 0 )
return 0;
return -1;
@@ -3342,12 +3339,13 @@
FT_CALLBACK_DEF( FT_UInt32* )
- tt_cmap14_variants( TT_CMap cmap,
+ tt_cmap14_variants( FT_CMap cmap, /* TT_CMap14 */
FT_Memory memory )
{
+ TT_CMap ttcmap = (TT_CMap)cmap;
TT_CMap14 cmap14 = (TT_CMap14)cmap;
FT_UInt32 count = cmap14->num_selectors;
- FT_Byte* p = cmap->data + 10;
+ FT_Byte* p = ttcmap->data + 10;
FT_UInt32* result;
FT_UInt32 i;
@@ -3368,13 +3366,14 @@
FT_CALLBACK_DEF( FT_UInt32 * )
- tt_cmap14_char_variants( TT_CMap cmap,
+ tt_cmap14_char_variants( FT_CMap cmap, /* TT_CMap14 */
FT_Memory memory,
FT_UInt32 charCode )
{
- TT_CMap14 cmap14 = (TT_CMap14) cmap;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ TT_CMap14 cmap14 = (TT_CMap14)cmap;
FT_UInt32 count = cmap14->num_selectors;
- FT_Byte* p = cmap->data + 10;
+ FT_Byte* p = ttcmap->data + 10;
FT_UInt32* q;
@@ -3388,12 +3387,12 @@
FT_ULong nondefOff = TT_NEXT_ULONG( p );
- if ( ( defOff != 0 &&
- tt_cmap14_char_map_def_binary( cmap->data + defOff,
- charCode ) ) ||
- ( nondefOff != 0 &&
- tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff,
- charCode ) != 0 ) )
+ if ( ( defOff != 0 &&
+ tt_cmap14_char_map_def_binary( ttcmap->data + defOff,
+ charCode ) ) ||
+ ( nondefOff != 0 &&
+ tt_cmap14_char_map_nondef_binary( ttcmap->data + nondefOff,
+ charCode ) != 0 ) )
{
q[0] = varSel;
q++;
@@ -3489,15 +3488,16 @@
FT_CALLBACK_DEF( FT_UInt32 * )
- tt_cmap14_variant_chars( TT_CMap cmap,
+ tt_cmap14_variant_chars( FT_CMap cmap, /* TT_CMap */
FT_Memory memory,
FT_UInt32 variantSelector )
{
- FT_Byte *p = tt_cmap14_find_variant( cmap->data + 6,
- variantSelector );
- FT_Int i;
- FT_ULong defOff;
- FT_ULong nondefOff;
+ TT_CMap ttcmap = (TT_CMap)cmap;
+ FT_Byte *p = tt_cmap14_find_variant( ttcmap->data + 6,
+ variantSelector );
+ FT_Int i;
+ FT_ULong defOff;
+ FT_ULong nondefOff;
if ( !p )
@@ -3510,16 +3510,16 @@
return NULL;
if ( defOff == 0 )
- return tt_cmap14_get_nondef_chars( cmap, cmap->data + nondefOff,
+ return tt_cmap14_get_nondef_chars( ttcmap, ttcmap->data + nondefOff,
memory );
else if ( nondefOff == 0 )
- return tt_cmap14_get_def_chars( cmap, cmap->data + defOff,
+ return tt_cmap14_get_def_chars( ttcmap, ttcmap->data + defOff,
memory );
else
{
/* Both a default and a non-default glyph set? That's probably not */
/* good font design, but the spec allows for it... */
- TT_CMap14 cmap14 = (TT_CMap14) cmap;
+ TT_CMap14 cmap14 = (TT_CMap14)cmap;
FT_UInt32 numRanges;
FT_UInt32 numMappings;
FT_UInt32 duni;
@@ -3531,18 +3531,18 @@
FT_UInt32 *ret;
- p = cmap->data + nondefOff;
- dp = cmap->data + defOff;
+ p = ttcmap->data + nondefOff;
+ dp = ttcmap->data + defOff;
numMappings = (FT_UInt32)TT_NEXT_ULONG( p );
dcnt = tt_cmap14_def_char_count( dp );
numRanges = (FT_UInt32)TT_NEXT_ULONG( dp );
if ( numMappings == 0 )
- return tt_cmap14_get_def_chars( cmap, cmap->data + defOff,
+ return tt_cmap14_get_def_chars( ttcmap, ttcmap->data + defOff,
memory );
if ( dcnt == 0 )
- return tt_cmap14_get_nondef_chars( cmap, cmap->data + nondefOff,
+ return tt_cmap14_get_nondef_chars( ttcmap, ttcmap->data + nondefOff,
memory );
if ( tt_cmap14_ensure( cmap14, ( dcnt + numMappings + 1 ), memory ) )
@@ -3664,9 +3664,10 @@
#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
FT_CALLBACK_DEF( const char * )
- tt_get_glyph_name( TT_Face face,
+ tt_get_glyph_name( void* face_, /* TT_Face */
FT_UInt idx )
{
+ TT_Face face = (TT_Face)face_;
FT_String* PSname = NULL;
@@ -3677,12 +3678,13 @@
FT_CALLBACK_DEF( FT_Error )
- tt_cmap_unicode_init( PS_Unicodes unicodes,
- FT_Pointer pointer )
+ tt_cmap_unicode_init( FT_CMap cmap, /* PS_Unicodes */
+ FT_Pointer pointer )
{
- TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
- FT_Memory memory = FT_FACE_MEMORY( face );
- FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
FT_UNUSED( pointer );
@@ -3693,17 +3695,18 @@
return psnames->unicodes_init( memory,
unicodes,
face->root.num_glyphs,
- (PS_GetGlyphNameFunc)&tt_get_glyph_name,
+ &tt_get_glyph_name,
(PS_FreeGlyphNameFunc)NULL,
(FT_Pointer)face );
}
FT_CALLBACK_DEF( void )
- tt_cmap_unicode_done( PS_Unicodes unicodes )
+ tt_cmap_unicode_done( FT_CMap cmap ) /* PS_Unicodes */
{
- FT_Face face = FT_CMAP_FACE( unicodes );
- FT_Memory memory = FT_FACE_MEMORY( face );
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ FT_Face face = FT_CMAP_FACE( cmap );
+ FT_Memory memory = FT_FACE_MEMORY( face );
FT_FREE( unicodes->maps );
@@ -3712,23 +3715,25 @@
FT_CALLBACK_DEF( FT_UInt )
- tt_cmap_unicode_char_index( PS_Unicodes unicodes,
- FT_UInt32 char_code )
+ tt_cmap_unicode_char_index( FT_CMap cmap, /* PS_Unicodes */
+ FT_UInt32 char_code )
{
- TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
- FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
return psnames->unicodes_char_index( unicodes, char_code );
}
- FT_CALLBACK_DEF( FT_UInt32 )
- tt_cmap_unicode_char_next( PS_Unicodes unicodes,
- FT_UInt32 *pchar_code )
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap_unicode_char_next( FT_CMap cmap, /* PS_Unicodes */
+ FT_UInt32 *pchar_code )
{
- TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
- FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+ PS_Unicodes unicodes = (PS_Unicodes)cmap;
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
return psnames->unicodes_char_next( unicodes, pchar_code );
@@ -3883,7 +3888,7 @@
tt_get_cmap_info( FT_CharMap charmap,
TT_CMapInfo *cmap_info )
{
- FT_CMap cmap = (FT_CMap)charmap;
+ FT_CMap cmap = FT_CMAP( charmap );
TT_CMap_Class clazz = (TT_CMap_Class)cmap->clazz;
diff --git a/thirdparty/freetype/src/sfnt/ttcolr.c b/thirdparty/freetype/src/sfnt/ttcolr.c
index 5d98dcab8f..69ccf0ee7a 100644
--- a/thirdparty/freetype/src/sfnt/ttcolr.c
+++ b/thirdparty/freetype/src/sfnt/ttcolr.c
@@ -699,7 +699,7 @@
item_deltas ) )
return 0;
- apaint->u.solid.color.alpha += item_deltas[0];
+ apaint->u.solid.color.alpha += (FT_F2Dot14)item_deltas[0];
}
#endif
@@ -1646,7 +1646,7 @@
return 0;
color_stop->stop_offset += F2DOT14_TO_FIXED( item_deltas[0] );
- color_stop->color.alpha += item_deltas[1];
+ color_stop->color.alpha += (FT_F2Dot14)item_deltas[1];
}
#else
FT_UNUSED( var_index_base );
@@ -1914,7 +1914,7 @@
#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
/* ANSI C doesn't like empty source files */
- typedef int _tt_colr_dummy;
+ typedef int tt_colr_dummy_;
#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
diff --git a/thirdparty/freetype/src/sfnt/ttcpal.c b/thirdparty/freetype/src/sfnt/ttcpal.c
index 4279bc0bd1..46ae08596f 100644
--- a/thirdparty/freetype/src/sfnt/ttcpal.c
+++ b/thirdparty/freetype/src/sfnt/ttcpal.c
@@ -303,7 +303,7 @@
#else /* !TT_CONFIG_OPTION_COLOR_LAYERS */
/* ANSI C doesn't like empty source files */
- typedef int _tt_cpal_dummy;
+ typedef int tt_cpal_dummy_;
#endif /* !TT_CONFIG_OPTION_COLOR_LAYERS */
diff --git a/thirdparty/freetype/src/sfnt/ttload.c b/thirdparty/freetype/src/sfnt/ttload.c
index 14f625c824..7b44e9cd2e 100644
--- a/thirdparty/freetype/src/sfnt/ttload.c
+++ b/thirdparty/freetype/src/sfnt/ttload.c
@@ -504,6 +504,13 @@
FT_FRAME_EXIT();
+ if ( !valid_entries )
+ {
+ FT_TRACE2(( "tt_face_load_font_dir: no valid tables found\n" ));
+ error = FT_THROW( Unknown_File_Format );
+ goto Exit;
+ }
+
FT_TRACE2(( "table directory loaded\n" ));
FT_TRACE2(( "\n" ));
diff --git a/thirdparty/freetype/src/sfnt/ttmtx.c b/thirdparty/freetype/src/sfnt/ttmtx.c
index 5e53e6dd4a..38ee9ae728 100644
--- a/thirdparty/freetype/src/sfnt/ttmtx.c
+++ b/thirdparty/freetype/src/sfnt/ttmtx.c
@@ -239,7 +239,7 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
FT_Service_MetricsVariations var =
- (FT_Service_MetricsVariations)face->var;
+ (FT_Service_MetricsVariations)face->tt_var;
#endif
diff --git a/thirdparty/freetype/src/sfnt/ttpost.c b/thirdparty/freetype/src/sfnt/ttpost.c
index 0e17c6f34a..1dfad4298b 100644
--- a/thirdparty/freetype/src/sfnt/ttpost.c
+++ b/thirdparty/freetype/src/sfnt/ttpost.c
@@ -156,86 +156,66 @@
static FT_Error
- load_format_20( TT_Face face,
- FT_Stream stream,
- FT_ULong post_len )
+ load_format_20( TT_Post_Names names,
+ FT_Stream stream,
+ FT_UShort num_glyphs,
+ FT_ULong post_len )
{
FT_Memory memory = stream->memory;
FT_Error error;
- FT_Int num_glyphs;
- FT_UShort num_names;
+ FT_UShort n;
+ FT_UShort num_names = 0;
FT_UShort* glyph_indices = NULL;
- FT_Char** name_strings = NULL;
- FT_Byte* strings = NULL;
+ FT_Byte** name_strings = NULL;
+ FT_Byte* q;
- if ( FT_READ_USHORT( num_glyphs ) )
- goto Exit;
-
- /* UNDOCUMENTED! The number of glyphs in this table can be smaller */
- /* than the value in the maxp table (cf. cyberbit.ttf). */
-
- /* There already exist fonts which have more than 32768 glyph names */
- /* in this table, so the test for this threshold has been dropped. */
-
- if ( num_glyphs > face->max_profile.numGlyphs ||
- (FT_ULong)num_glyphs * 2UL > post_len - 2 )
+ if ( (FT_ULong)num_glyphs * 2 > post_len )
{
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
- /* load the indices */
- {
- FT_Int n;
-
-
- if ( FT_QNEW_ARRAY( glyph_indices, num_glyphs ) ||
- FT_FRAME_ENTER( num_glyphs * 2L ) )
- goto Fail;
-
- for ( n = 0; n < num_glyphs; n++ )
- glyph_indices[n] = FT_GET_USHORT();
+ /* load the indices and note their maximum */
+ if ( FT_QNEW_ARRAY( glyph_indices, num_glyphs ) ||
+ FT_FRAME_ENTER( num_glyphs * 2 ) )
+ goto Fail;
- FT_FRAME_EXIT();
- }
+ q = (FT_Byte*)stream->cursor;
- /* compute number of names stored in table */
+ for ( n = 0; n < num_glyphs; n++ )
{
- FT_Int n;
+ FT_UShort idx = FT_NEXT_USHORT( q );
- num_names = 0;
+ if ( idx > num_names )
+ num_names = idx;
- for ( n = 0; n < num_glyphs; n++ )
- {
- FT_Int idx;
+ glyph_indices[n] = idx;
+ }
+ FT_FRAME_EXIT();
- idx = glyph_indices[n];
- if ( idx >= 258 )
- {
- idx -= 257;
- if ( idx > num_names )
- num_names = (FT_UShort)idx;
- }
- }
- }
+ /* compute number of names stored in the table */
+ num_names = num_names > 257 ? num_names - 257 : 0;
/* now load the name strings */
if ( num_names )
{
- FT_UShort n;
FT_ULong p;
+ FT_Byte* strings;
- post_len -= (FT_ULong)num_glyphs * 2UL + 2;
+ post_len -= (FT_ULong)num_glyphs * 2;
+
+ if ( FT_QALLOC( name_strings, num_names * sizeof ( FT_Byte* ) +
+ post_len + 1 ) )
+ goto Fail;
- if ( FT_QALLOC( strings, post_len + 1 ) ||
- FT_STREAM_READ( strings, post_len ) ||
- FT_QNEW_ARRAY( name_strings, num_names ) )
+ strings = (FT_Byte*)( name_strings + num_names );
+ if ( FT_STREAM_READ( strings, post_len ) )
goto Fail;
/* convert from Pascal- to C-strings and set pointers */
@@ -251,7 +231,7 @@
}
strings[p] = 0;
- name_strings[n] = (FT_Char*)strings + p + 1;
+ name_strings[n] = strings + p + 1;
p += len + 1;
}
strings[post_len] = 0;
@@ -259,40 +239,24 @@
/* deal with missing or insufficient string data */
if ( n < num_names )
{
- if ( post_len == 0 )
- {
- /* fake empty string */
- if ( FT_QREALLOC( strings, 1, 2 ) )
- goto Fail;
-
- post_len = 1;
- strings[post_len] = 0;
- }
+ FT_TRACE4(( "load_format_20: %hu PostScript names are truncated\n",
+ num_names - n ));
- FT_ERROR(( "load_format_20:"
- " all entries in post table are already parsed,"
- " using NULL names for gid %d - %d\n",
- n, num_names - 1 ));
for ( ; n < num_names; n++ )
- name_strings[n] = (FT_Char*)strings + post_len;
+ name_strings[n] = strings + post_len;
}
}
/* all right, set table fields and exit successfully */
- {
- TT_Post_20 table = &face->postscript_names.names.format_20;
-
+ names->num_glyphs = num_glyphs;
+ names->num_names = num_names;
+ names->glyph_indices = glyph_indices;
+ names->glyph_names = name_strings;
- table->num_glyphs = (FT_UShort)num_glyphs;
- table->num_names = (FT_UShort)num_names;
- table->glyph_indices = glyph_indices;
- table->glyph_names = name_strings;
- }
return FT_Err_Ok;
Fail:
FT_FREE( name_strings );
- FT_FREE( strings );
FT_FREE( glyph_indices );
Exit:
@@ -301,66 +265,55 @@
static FT_Error
- load_format_25( TT_Face face,
- FT_Stream stream,
- FT_ULong post_len )
+ load_format_25( TT_Post_Names names,
+ FT_Stream stream,
+ FT_UShort num_glyphs,
+ FT_ULong post_len )
{
FT_Memory memory = stream->memory;
FT_Error error;
- FT_Int num_glyphs;
- FT_Char* offset_table = NULL;
-
- FT_UNUSED( post_len );
+ FT_UShort n;
+ FT_UShort* glyph_indices = NULL;
+ FT_Byte* q;
- if ( FT_READ_USHORT( num_glyphs ) )
- goto Exit;
-
- /* check the number of glyphs */
- if ( num_glyphs > face->max_profile.numGlyphs ||
- num_glyphs > 258 ||
- num_glyphs < 1 )
+ /* check the number of glyphs, including the theoretical limit */
+ if ( num_glyphs > post_len ||
+ num_glyphs > 258 + 128 )
{
error = FT_THROW( Invalid_File_Format );
goto Exit;
}
- if ( FT_QNEW_ARRAY( offset_table, num_glyphs ) ||
- FT_STREAM_READ( offset_table, num_glyphs ) )
+ /* load the indices and check their Mac range */
+ if ( FT_QNEW_ARRAY( glyph_indices, num_glyphs ) ||
+ FT_FRAME_ENTER( num_glyphs ) )
goto Fail;
- /* now check the offset table */
- {
- FT_Int n;
+ q = (FT_Byte*)stream->cursor;
+ for ( n = 0; n < num_glyphs; n++ )
+ {
+ FT_Int idx = n + FT_NEXT_CHAR( q );
- for ( n = 0; n < num_glyphs; n++ )
- {
- FT_Long idx = (FT_Long)n + offset_table[n];
+ if ( idx < 0 || idx > 257 )
+ idx = 0;
- if ( idx < 0 || idx > num_glyphs )
- {
- error = FT_THROW( Invalid_File_Format );
- goto Fail;
- }
- }
+ glyph_indices[n] = (FT_UShort)idx;
}
- /* OK, set table fields and exit successfully */
- {
- TT_Post_25 table = &face->postscript_names.names.format_25;
-
+ FT_FRAME_EXIT();
- table->num_glyphs = (FT_UShort)num_glyphs;
- table->offsets = offset_table;
- }
+ /* OK, set table fields and exit successfully */
+ names->num_glyphs = num_glyphs;
+ names->glyph_indices = glyph_indices;
return FT_Err_Ok;
Fail:
- FT_FREE( offset_table );
+ FT_FREE( glyph_indices );
Exit:
return error;
@@ -370,37 +323,37 @@
static FT_Error
load_post_names( TT_Face face )
{
- FT_Stream stream;
- FT_Error error;
- FT_Fixed format;
+ FT_Error error = FT_Err_Ok;
+ FT_Stream stream = face->root.stream;
+ FT_Fixed format = face->postscript.FormatType;
FT_ULong post_len;
+ FT_UShort num_glyphs;
- /* get a stream for the face's resource */
- stream = face->root.stream;
-
/* seek to the beginning of the PS names table */
error = face->goto_table( face, TTAG_post, stream, &post_len );
if ( error )
goto Exit;
- format = face->postscript.FormatType;
-
- /* go to beginning of subtable */
- if ( FT_STREAM_SKIP( 32 ) )
+ /* UNDOCUMENTED! The number of glyphs in this table can be smaller */
+ /* than the value in the maxp table (cf. cyberbit.ttf). */
+ if ( post_len < 34 ||
+ FT_STREAM_SKIP( 32 ) ||
+ FT_READ_USHORT( num_glyphs ) ||
+ num_glyphs > face->max_profile.numGlyphs ||
+ num_glyphs == 0 )
goto Exit;
- /* now read postscript table */
- if ( format == 0x00020000L && post_len >= 34 )
- error = load_format_20( face, stream, post_len - 32 );
- else if ( format == 0x00025000L && post_len >= 34 )
- error = load_format_25( face, stream, post_len - 32 );
- else
- error = FT_THROW( Invalid_File_Format );
-
- face->postscript_names.loaded = 1;
+ /* now read postscript names data */
+ if ( format == 0x00020000L )
+ error = load_format_20( &face->postscript_names, stream,
+ num_glyphs, post_len - 34 );
+ else if ( format == 0x00025000L )
+ error = load_format_25( &face->postscript_names, stream,
+ num_glyphs, post_len - 34 );
Exit:
+ face->postscript_names.loaded = 1; /* even if failed */
return error;
}
@@ -410,39 +363,20 @@
{
FT_Memory memory = face->root.memory;
TT_Post_Names names = &face->postscript_names;
- FT_Fixed format;
- if ( names->loaded )
+ if ( names->num_glyphs )
{
- format = face->postscript.FormatType;
-
- if ( format == 0x00020000L )
- {
- TT_Post_20 table = &names->names.format_20;
-
-
- FT_FREE( table->glyph_indices );
- table->num_glyphs = 0;
-
- if ( table->num_names )
- {
- table->glyph_names[0]--;
- FT_FREE( table->glyph_names[0] );
-
- FT_FREE( table->glyph_names );
- table->num_names = 0;
- }
- }
- else if ( format == 0x00025000L )
- {
- TT_Post_25 table = &names->names.format_25;
-
+ FT_FREE( names->glyph_indices );
+ names->num_glyphs = 0;
+ }
- FT_FREE( table->offsets );
- table->num_glyphs = 0;
- }
+ if ( names->num_names )
+ {
+ FT_FREE( names->glyph_names );
+ names->num_names = 0;
}
+
names->loaded = 0;
}
@@ -478,7 +412,6 @@
FT_String** PSname )
{
FT_Error error;
- TT_Post_Names names;
FT_Fixed format;
#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
@@ -498,8 +431,6 @@
return FT_THROW( Unimplemented_Feature );
#endif
- names = &face->postscript_names;
-
/* `.notdef' by default */
*PSname = MAC_NAME( 0 );
@@ -510,9 +441,10 @@
if ( idx < 258 ) /* paranoid checking */
*PSname = MAC_NAME( idx );
}
- else if ( format == 0x00020000L )
+ else if ( format == 0x00020000L ||
+ format == 0x00025000L )
{
- TT_Post_20 table = &names->names.format_20;
+ TT_Post_Names names = &face->postscript_names;
if ( !names->loaded )
@@ -522,43 +454,29 @@
goto End;
}
- if ( idx < (FT_UInt)table->num_glyphs )
+ if ( idx < (FT_UInt)names->num_glyphs )
{
- FT_UShort name_index = table->glyph_indices[idx];
+ FT_UShort name_index = names->glyph_indices[idx];
if ( name_index < 258 )
*PSname = MAC_NAME( name_index );
- else
- *PSname = (FT_String*)table->glyph_names[name_index - 258];
- }
- }
- else if ( format == 0x00025000L )
- {
- TT_Post_25 table = &names->names.format_25;
-
-
- if ( !names->loaded )
- {
- error = load_post_names( face );
- if ( error )
- goto End;
+ else /* only for version 2.0 */
+ *PSname = (FT_String*)names->glyph_names[name_index - 258];
}
-
- if ( idx < (FT_UInt)table->num_glyphs ) /* paranoid checking */
- *PSname = MAC_NAME( (FT_Int)idx + table->offsets[idx] );
}
/* nothing to do for format == 0x00030000L */
End:
+ /* post format errors ignored */
return FT_Err_Ok;
}
#else /* !TT_CONFIG_OPTION_POSTSCRIPT_NAMES */
/* ANSI C doesn't like empty source files */
- typedef int _tt_post_dummy;
+ typedef int tt_post_dummy_;
#endif /* !TT_CONFIG_OPTION_POSTSCRIPT_NAMES */
diff --git a/thirdparty/freetype/src/sfnt/ttsbit.c b/thirdparty/freetype/src/sfnt/ttsbit.c
index 3c06955131..03f90a628d 100644
--- a/thirdparty/freetype/src/sfnt/ttsbit.c
+++ b/thirdparty/freetype/src/sfnt/ttsbit.c
@@ -1677,7 +1677,7 @@
#else /* !TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
/* ANSI C doesn't like empty source files */
- typedef int _tt_sbit_dummy;
+ typedef int tt_sbit_dummy_;
#endif /* !TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
diff --git a/thirdparty/freetype/src/sfnt/ttsvg.c b/thirdparty/freetype/src/sfnt/ttsvg.c
index c1bbb66b81..4461d483b0 100644
--- a/thirdparty/freetype/src/sfnt/ttsvg.c
+++ b/thirdparty/freetype/src/sfnt/ttsvg.c
@@ -405,7 +405,7 @@
#else /* !FT_CONFIG_OPTION_SVG */
/* ANSI C doesn't like empty source files */
- typedef int _tt_svg_dummy;
+ typedef int tt_svg_dummy_;
#endif /* !FT_CONFIG_OPTION_SVG */
diff --git a/thirdparty/freetype/src/sfnt/woff2tags.c b/thirdparty/freetype/src/sfnt/woff2tags.c
index 7a0a351f06..eeedd9906b 100644
--- a/thirdparty/freetype/src/sfnt/woff2tags.c
+++ b/thirdparty/freetype/src/sfnt/woff2tags.c
@@ -111,7 +111,7 @@
#else /* !FT_CONFIG_OPTION_USE_BROTLI */
/* ANSI C doesn't like empty source files */
- typedef int _woff2tags_dummy;
+ typedef int woff2tags_dummy_;
#endif /* !FT_CONFIG_OPTION_USE_BROTLI */
diff --git a/thirdparty/freetype/src/smooth/ftgrays.c b/thirdparty/freetype/src/smooth/ftgrays.c
index d9f20eef13..6252df98ae 100644
--- a/thirdparty/freetype/src/smooth/ftgrays.c
+++ b/thirdparty/freetype/src/smooth/ftgrays.c
@@ -1006,9 +1006,9 @@ typedef ptrdiff_t FT_PtrDist;
*
* For other cases, using binary splits is actually slightly faster.
*/
-#if defined( __SSE2__ ) || \
- defined( __x86_64__ ) || \
- defined( _M_AMD64 ) || \
+#if defined( __SSE2__ ) || \
+ ( defined( __x86_64__ ) && !defined( __VMS ) ) || \
+ defined( _M_AMD64 ) || \
( defined( _M_IX86_FP ) && _M_IX86_FP >= 2 )
# define FT_SSE2 1
#else
@@ -1427,8 +1427,10 @@ typedef ptrdiff_t FT_PtrDist;
static int
gray_move_to( const FT_Vector* to,
- gray_PWorker worker )
+ void* worker_ ) /* gray_PWorker */
{
+ gray_PWorker worker = (gray_PWorker)worker_;
+
TPos x, y;
@@ -1446,8 +1448,11 @@ typedef ptrdiff_t FT_PtrDist;
static int
gray_line_to( const FT_Vector* to,
- gray_PWorker worker )
+ void* worker_ ) /* gray_PWorker */
{
+ gray_PWorker worker = (gray_PWorker)worker_;
+
+
gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) );
return 0;
}
@@ -1456,8 +1461,11 @@ typedef ptrdiff_t FT_PtrDist;
static int
gray_conic_to( const FT_Vector* control,
const FT_Vector* to,
- gray_PWorker worker )
+ void* worker_ ) /* gray_PWorker */
{
+ gray_PWorker worker = (gray_PWorker)worker_;
+
+
gray_render_conic( RAS_VAR_ control, to );
return 0;
}
@@ -1467,8 +1475,11 @@ typedef ptrdiff_t FT_PtrDist;
gray_cubic_to( const FT_Vector* control1,
const FT_Vector* control2,
const FT_Vector* to,
- gray_PWorker worker )
+ void* worker_ ) /* gray_PWorker */
{
+ gray_PWorker worker = (gray_PWorker)worker_;
+
+
gray_render_cubic( RAS_VAR_ control1, control2, to );
return 0;
}
@@ -1666,6 +1677,8 @@ typedef ptrdiff_t FT_PtrDist;
int n; /* index of contour in outline */
int first; /* index of first point in contour */
+ int last; /* index of last point in contour */
+
char tag; /* current point's state */
int shift;
@@ -1680,18 +1693,17 @@ typedef ptrdiff_t FT_PtrDist;
shift = func_interface->shift;
delta = func_interface->delta;
- first = 0;
+ last = -1;
for ( n = 0; n < outline->n_contours; n++ )
{
- int last; /* index of last point in contour */
-
-
- FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n ));
+ FT_TRACE5(( "FT_Outline_Decompose: Contour %d\n", n ));
+ first = last + 1;
last = outline->contours[n];
- if ( last < 0 )
+ if ( last < first )
goto Invalid_Outline;
+
limit = outline->points + last;
v_start = outline->points[first];
@@ -1874,11 +1886,9 @@ typedef ptrdiff_t FT_PtrDist;
v_start.x / 64.0, v_start.y / 64.0 ));
error = func_interface->line_to( &v_start, user );
- Close:
+ Close:
if ( error )
goto Exit;
-
- first = last + 1;
}
FT_TRACE5(( "FT_Outline_Decompose: Done\n", n ));
@@ -2156,9 +2166,12 @@ typedef ptrdiff_t FT_PtrDist;
#else /* !STANDALONE_ */
static int
- gray_raster_new( FT_Memory memory,
- gray_PRaster* araster )
+ gray_raster_new( void* memory_,
+ FT_Raster* araster_ )
{
+ FT_Memory memory = (FT_Memory)memory_;
+ gray_PRaster* araster = (gray_PRaster*)araster_;
+
FT_Error error;
gray_PRaster raster = NULL;
diff --git a/thirdparty/freetype/src/smooth/ftsmooth.c b/thirdparty/freetype/src/smooth/ftsmooth.c
index cdbc78c3e5..9b0e8886cb 100644
--- a/thirdparty/freetype/src/smooth/ftsmooth.c
+++ b/thirdparty/freetype/src/smooth/ftsmooth.c
@@ -87,8 +87,10 @@
/* initialize renderer -- init its raster */
static FT_Error
- ft_smooth_init( FT_Renderer render )
+ ft_smooth_init( FT_Module module ) /* FT_Renderer */
{
+ FT_Renderer render = (FT_Renderer)module;
+
FT_Vector* sub = render->root.library->lcd_geometry;
@@ -111,8 +113,10 @@
ft_smooth_lcd_spans( int y,
int count,
const FT_Span* spans,
- TOrigin* target )
+ void* target_ ) /* TOrigin* */
{
+ TOrigin* target = (TOrigin*)target_;
+
unsigned char* dst_line = target->origin - y * target->pitch;
unsigned char* dst;
unsigned short w;
@@ -141,7 +145,7 @@
/* Set up direct rendering to record them on each third byte. */
params.source = outline;
params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT;
- params.gray_spans = (FT_SpanFunc)ft_smooth_lcd_spans;
+ params.gray_spans = ft_smooth_lcd_spans;
params.user = &target;
params.clip_box.xMin = 0;
@@ -256,8 +260,11 @@
/* initialize renderer -- init its raster */
static FT_Error
- ft_smooth_init( FT_Renderer render )
+ ft_smooth_init( FT_Module module ) /* FT_Renderer */
{
+ FT_Renderer render = (FT_Renderer)module;
+
+
/* set up default LCD filtering */
FT_Library_SetLcdFilter( render->root.library, FT_LCD_FILTER_DEFAULT );
@@ -340,8 +347,11 @@
ft_smooth_overlap_spans( int y,
int count,
const FT_Span* spans,
- TOrigin* target )
+ void* target_ )
{
+ TOrigin* target = (TOrigin*)target_;
+
+
unsigned char* dst = target->origin - ( y / SCALE ) * target->pitch;
unsigned short x;
unsigned int cover, sum;
@@ -386,7 +396,7 @@
/* Set up direct rendering to average oversampled spans. */
params.source = outline;
params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT;
- params.gray_spans = (FT_SpanFunc)ft_smooth_overlap_spans;
+ params.gray_spans = ft_smooth_overlap_spans;
params.user = &target;
params.clip_box.xMin = 0;
diff --git a/thirdparty/freetype/src/svg/ftsvg.c b/thirdparty/freetype/src/svg/ftsvg.c
index 7edb1a338e..ba237f6380 100644
--- a/thirdparty/freetype/src/svg/ftsvg.c
+++ b/thirdparty/freetype/src/svg/ftsvg.c
@@ -40,26 +40,31 @@
/* ft_svg_init */
static FT_Error
- ft_svg_init( SVG_Renderer svg_module )
+ ft_svg_init( FT_Module module )
{
+ SVG_Renderer render = (SVG_Renderer)module;
+
FT_Error error = FT_Err_Ok;
- svg_module->loaded = FALSE;
- svg_module->hooks_set = FALSE;
+ render->loaded = FALSE;
+ render->hooks_set = FALSE;
return error;
}
static void
- ft_svg_done( SVG_Renderer svg_module )
+ ft_svg_done( FT_Module module )
{
- if ( svg_module->loaded == TRUE &&
- svg_module->hooks_set == TRUE )
- svg_module->hooks.free_svg( &svg_module->state );
+ SVG_Renderer render = (SVG_Renderer)module;
+
+
+ if ( render->loaded == TRUE &&
+ render->hooks_set == TRUE )
+ render->hooks.free_svg( &render->state );
- svg_module->loaded = FALSE;
+ render->loaded = FALSE;
}
@@ -148,7 +153,7 @@
static const SVG_Interface svg_interface =
{
- (Preset_Bitmap_Func)ft_svg_preset_slot
+ ft_svg_preset_slot /* Preset_Bitmap_Func preset_slot */
};
@@ -203,7 +208,7 @@
static FT_Error
ft_svg_property_get( FT_Module module,
const char* property_name,
- const void* value )
+ void* value )
{
FT_Error error = FT_Err_Ok;
SVG_Renderer renderer = (SVG_Renderer)module;
@@ -226,8 +231,8 @@
FT_DEFINE_SERVICE_PROPERTIESREC(
ft_svg_service_properties,
- (FT_Properties_SetFunc)ft_svg_property_set, /* set_property */
- (FT_Properties_GetFunc)ft_svg_property_get /* get_property */
+ ft_svg_property_set, /* FT_Properties_SetFunc set_property */
+ ft_svg_property_get /* FT_Properties_GetFunc get_property */
)
@@ -333,17 +338,17 @@
(const void*)PUT_SVG_MODULE( &svg_interface ), /* module specific interface */
- (FT_Module_Constructor)PUT_SVG_MODULE( ft_svg_init ), /* module_init */
- (FT_Module_Destructor)PUT_SVG_MODULE( ft_svg_done ), /* module_done */
- PUT_SVG_MODULE( ft_svg_get_interface ), /* get_interface */
+ PUT_SVG_MODULE( ft_svg_init ), /* FT_Module_Constructor module_init */
+ PUT_SVG_MODULE( ft_svg_done ), /* FT_Module_Destructor module_done */
+ PUT_SVG_MODULE( ft_svg_get_interface ), /* FT_Module_Requester get_interface */
SVG_GLYPH_FORMAT,
- (FT_Renderer_RenderFunc) PUT_SVG_MODULE( ft_svg_render ), /* render_glyph */
- (FT_Renderer_TransformFunc)PUT_SVG_MODULE( ft_svg_transform ), /* transform_glyph */
- NULL, /* get_glyph_cbox */
- NULL, /* set_mode */
- NULL /* raster_class */
+ PUT_SVG_MODULE( ft_svg_render ), /* FT_Renderer_RenderFunc render_glyph */
+ PUT_SVG_MODULE( ft_svg_transform ), /* FT_Renderer_TransformFunc transform_glyph */
+ NULL, /* FT_Renderer_GetCBoxFunc get_glyph_cbox */
+ NULL, /* FT_Renderer_SetModeFunc set_mode */
+ NULL /* FT_Raster_Funcs* raster_class */
)
diff --git a/thirdparty/freetype/src/truetype/ttdriver.c b/thirdparty/freetype/src/truetype/ttdriver.c
index 4bea63ef84..7151e82073 100644
--- a/thirdparty/freetype/src/truetype/ttdriver.c
+++ b/thirdparty/freetype/src/truetype/ttdriver.c
@@ -57,7 +57,7 @@
* PROPERTY SERVICE
*
*/
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
tt_property_set( FT_Module module, /* TT_Driver */
const char* property_name,
const void* value,
@@ -93,17 +93,27 @@
interpreter_version = *iv;
}
- if ( interpreter_version == TT_INTERPRETER_VERSION_35
+ switch ( interpreter_version )
+ {
+ case TT_INTERPRETER_VERSION_35:
+ driver->interpreter_version = TT_INTERPRETER_VERSION_35;
+ break;
+
+ case TT_INTERPRETER_VERSION_38:
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
- || interpreter_version == TT_INTERPRETER_VERSION_38
+ driver->interpreter_version = TT_INTERPRETER_VERSION_38;
+ break;
#endif
+
+ case TT_INTERPRETER_VERSION_40:
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
- || interpreter_version == TT_INTERPRETER_VERSION_40
+ driver->interpreter_version = TT_INTERPRETER_VERSION_40;
+ break;
#endif
- )
- driver->interpreter_version = interpreter_version;
- else
+
+ default:
error = FT_ERR( Unimplemented_Feature );
+ }
return error;
}
@@ -114,10 +124,10 @@
}
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
tt_property_get( FT_Module module, /* TT_Driver */
const char* property_name,
- const void* value )
+ void* value )
{
FT_Error error = FT_Err_Ok;
TT_Driver driver = (TT_Driver)module;
@@ -144,8 +154,8 @@
FT_DEFINE_SERVICE_PROPERTIESREC(
tt_service_properties,
- (FT_Properties_SetFunc)tt_property_set, /* set_property */
- (FT_Properties_GetFunc)tt_property_get /* get_property */
+ tt_property_set, /* FT_Properties_SetFunc set_property */
+ tt_property_get /* FT_Properties_GetFunc get_property */
)
@@ -198,35 +208,35 @@
*
* They can be implemented by format-specific interfaces.
*/
- static FT_Error
- tt_get_kerning( FT_Face ttface, /* TT_Face */
+ FT_CALLBACK_DEF( FT_Error )
+ tt_get_kerning( FT_Face face, /* TT_Face */
FT_UInt left_glyph,
FT_UInt right_glyph,
FT_Vector* kerning )
{
- TT_Face face = (TT_Face)ttface;
- SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+ TT_Face ttface = (TT_Face)face;
+ SFNT_Service sfnt = (SFNT_Service)ttface->sfnt;
kerning->x = 0;
kerning->y = 0;
if ( sfnt )
- kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph );
+ kerning->x = sfnt->get_kerning( ttface, left_glyph, right_glyph );
return 0;
}
- static FT_Error
- tt_get_advances( FT_Face ttface,
+ FT_CALLBACK_DEF( FT_Error )
+ tt_get_advances( FT_Face face, /* TT_Face */
FT_UInt start,
FT_UInt count,
FT_Int32 flags,
FT_Fixed *advances )
{
FT_UInt nn;
- TT_Face face = (TT_Face)ttface;
+ TT_Face ttface = (TT_Face)face;
/* XXX: TODO: check for sbits */
@@ -235,8 +245,8 @@
{
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
/* no fast retrieval for blended MM fonts without VVAR table */
- if ( ( FT_IS_NAMED_INSTANCE( ttface ) || FT_IS_VARIATION( ttface ) ) &&
- !( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
+ if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) &&
+ !( ttface->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
return FT_THROW( Unimplemented_Feature );
#endif
@@ -247,7 +257,7 @@
/* since we don't need `tsb', we use zero for `yMax' parameter */
- TT_Get_VMetrics( face, start + nn, 0, &tsb, &ah );
+ TT_Get_VMetrics( ttface, start + nn, 0, &tsb, &ah );
advances[nn] = ah;
}
}
@@ -255,8 +265,8 @@
{
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
/* no fast retrieval for blended MM fonts without HVAR table */
- if ( ( FT_IS_NAMED_INSTANCE( ttface ) || FT_IS_VARIATION( ttface ) ) &&
- !( face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
+ if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) &&
+ !( ttface->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
return FT_THROW( Unimplemented_Feature );
#endif
@@ -266,7 +276,7 @@
FT_UShort aw;
- TT_Get_HMetrics( face, start + nn, &lsb, &aw );
+ TT_Get_HMetrics( ttface, start + nn, &lsb, &aw );
advances[nn] = aw;
}
}
@@ -290,7 +300,7 @@
#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
tt_size_select( FT_Size size,
FT_ULong strike_index )
{
@@ -306,7 +316,7 @@
/* use the scaled metrics, even when tt_size_reset fails */
FT_Select_Metrics( size->face, strike_index );
- tt_size_reset( ttsize, 0 ); /* ignore return value */
+ tt_size_reset( ttsize ); /* ignore return value */
}
else
{
@@ -327,7 +337,7 @@
#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
tt_size_request( FT_Size size,
FT_Size_Request req )
{
@@ -367,7 +377,7 @@
if ( FT_IS_SCALABLE( size->face ) )
{
- error = tt_size_reset( ttsize, 0 );
+ error = tt_size_reset( ttsize );
#ifdef TT_USE_BYTECODE_INTERPRETER
/* for the `MPS' bytecode instruction we need the point size */
@@ -426,15 +436,15 @@
* @Return:
* FreeType error code. 0 means success.
*/
- static FT_Error
- tt_glyph_load( FT_GlyphSlot ttslot, /* TT_GlyphSlot */
- FT_Size ttsize, /* TT_Size */
+ FT_CALLBACK_DEF( FT_Error )
+ tt_glyph_load( FT_GlyphSlot slot, /* TT_GlyphSlot */
+ FT_Size size, /* TT_Size */
FT_UInt glyph_index,
FT_Int32 load_flags )
{
- TT_GlyphSlot slot = (TT_GlyphSlot)ttslot;
- TT_Size size = (TT_Size)ttsize;
- FT_Face face = ttslot->face;
+ TT_GlyphSlot ttslot = (TT_GlyphSlot)slot;
+ TT_Size ttsize = (TT_Size)size;
+ FT_Face face = ttslot->face;
FT_Error error;
@@ -476,12 +486,12 @@
}
/* use hinted metrics only if we load a glyph with hinting */
- size->metrics = ( load_flags & FT_LOAD_NO_HINTING )
- ? &ttsize->metrics
- : &size->hinted_metrics;
+ ttsize->metrics = ( load_flags & FT_LOAD_NO_HINTING )
+ ? &size->metrics
+ : &ttsize->hinted_metrics;
/* now fill in the glyph slot with outline/bitmap/layered */
- error = TT_Load_Glyph( size, slot, glyph_index, load_flags );
+ error = TT_Load_Glyph( ttsize, ttslot, glyph_index, load_flags );
/* force drop-out mode to 2 - irrelevant now */
/* slot->outline.dropout_mode = 2; */
@@ -507,49 +517,47 @@
FT_DEFINE_SERVICE_MULTIMASTERSREC(
tt_service_gx_multi_masters,
- (FT_Get_MM_Func) NULL, /* get_mm */
- (FT_Set_MM_Design_Func) NULL, /* set_mm_design */
- (FT_Set_MM_Blend_Func) TT_Set_MM_Blend, /* set_mm_blend */
- (FT_Get_MM_Blend_Func) TT_Get_MM_Blend, /* get_mm_blend */
- (FT_Get_MM_Var_Func) TT_Get_MM_Var, /* get_mm_var */
- (FT_Set_Var_Design_Func)TT_Set_Var_Design, /* set_var_design */
- (FT_Get_Var_Design_Func)TT_Get_Var_Design, /* get_var_design */
- (FT_Set_Instance_Func) TT_Set_Named_Instance, /* set_instance */
- (FT_Set_MM_WeightVector_Func)
- NULL, /* set_mm_weightvector */
- (FT_Get_MM_WeightVector_Func)
- NULL, /* get_mm_weightvector */
- (FT_Var_Load_Delta_Set_Idx_Map_Func)
- tt_var_load_delta_set_index_mapping,
- /* load_delta_set_idx_map */
- (FT_Var_Load_Item_Var_Store_Func)
- tt_var_load_item_variation_store,
- /* load_item_variation_store */
- (FT_Var_Get_Item_Delta_Func)
- tt_var_get_item_delta, /* get_item_delta */
- (FT_Var_Done_Item_Var_Store_Func)
- tt_var_done_item_variation_store,
- /* done_item_variation_store */
- (FT_Var_Done_Delta_Set_Idx_Map_Func)
- tt_var_done_delta_set_index_map,
- /* done_delta_set_index_map */
- (FT_Get_Var_Blend_Func) tt_get_var_blend, /* get_var_blend */
- (FT_Done_Blend_Func) tt_done_blend /* done_blend */
+ NULL, /* FT_Get_MM_Func get_mm */
+ NULL, /* FT_Set_MM_Design_Func set_mm_design */
+ TT_Set_MM_Blend, /* FT_Set_MM_Blend_Func set_mm_blend */
+ TT_Get_MM_Blend, /* FT_Get_MM_Blend_Func get_mm_blend */
+ TT_Get_MM_Var, /* FT_Get_MM_Var_Func get_mm_var */
+ TT_Set_Var_Design, /* FT_Set_Var_Design_Func set_var_design */
+ TT_Get_Var_Design, /* FT_Get_Var_Design_Func get_var_design */
+ TT_Set_Named_Instance, /* FT_Set_Named_Instance_Func set_named_instance */
+ TT_Get_Default_Named_Instance,
+ /* FT_Get_Default_Named_Instance_Func get_default_named_instance */
+ NULL, /* FT_Set_MM_WeightVector_Func set_mm_weightvector */
+ NULL, /* FT_Get_MM_WeightVector_Func get_mm_weightvector */
+
+ tt_construct_ps_name, /* FT_Construct_PS_Name_Func construct_ps_name */
+ tt_var_load_delta_set_index_mapping,
+ /* FT_Var_Load_Delta_Set_Idx_Map_Func load_delta_set_idx_map */
+ tt_var_load_item_variation_store,
+ /* FT_Var_Load_Item_Var_Store_Func load_item_variation_store */
+ tt_var_get_item_delta, /* FT_Var_Get_Item_Delta_Func get_item_delta */
+ tt_var_done_item_variation_store,
+ /* FT_Var_Done_Item_Var_Store_Func done_item_variation_store */
+ tt_var_done_delta_set_index_map,
+ /* FT_Var_Done_Delta_Set_Idx_Map_Func done_delta_set_index_map */
+ tt_get_var_blend, /* FT_Get_Var_Blend_Func get_var_blend */
+ tt_done_blend /* FT_Done_Blend_Func done_blend */
)
FT_DEFINE_SERVICE_METRICSVARIATIONSREC(
tt_service_metrics_variations,
- (FT_HAdvance_Adjust_Func)tt_hadvance_adjust, /* hadvance_adjust */
- (FT_LSB_Adjust_Func) NULL, /* lsb_adjust */
- (FT_RSB_Adjust_Func) NULL, /* rsb_adjust */
+ tt_hadvance_adjust, /* FT_HAdvance_Adjust_Func hadvance_adjust */
+ NULL, /* FT_LSB_Adjust_Func lsb_adjust */
+ NULL, /* FT_RSB_Adjust_Func rsb_adjust */
- (FT_VAdvance_Adjust_Func)tt_vadvance_adjust, /* vadvance_adjust */
- (FT_TSB_Adjust_Func) NULL, /* tsb_adjust */
- (FT_BSB_Adjust_Func) NULL, /* bsb_adjust */
- (FT_VOrg_Adjust_Func) NULL, /* vorg_adjust */
+ tt_vadvance_adjust, /* FT_VAdvance_Adjust_Func vadvance_adjust */
+ NULL, /* FT_TSB_Adjust_Func tsb_adjust */
+ NULL, /* FT_BSB_Adjust_Func bsb_adjust */
+ NULL, /* FT_VOrg_Adjust_Func vorg_adjust */
- (FT_Metrics_Adjust_Func) tt_apply_mvar /* metrics_adjust */
+ tt_apply_mvar, /* FT_Metrics_Adjust_Func metrics_adjust */
+ tt_size_reset_height /* FT_Size_Reset_Func size_reset */
)
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
diff --git a/thirdparty/freetype/src/truetype/ttgload.c b/thirdparty/freetype/src/truetype/ttgload.c
index d33bdad642..5f15a7f4de 100644
--- a/thirdparty/freetype/src/truetype/ttgload.c
+++ b/thirdparty/freetype/src/truetype/ttgload.c
@@ -362,17 +362,16 @@
FT_Byte* p = load->cursor;
FT_Byte* limit = load->limit;
FT_GlyphLoader gloader = load->gloader;
+ FT_Outline* outline = &gloader->current.outline;
FT_Int n_contours = load->n_contours;
- FT_Outline* outline;
- FT_UShort n_ins;
FT_Int n_points;
+ FT_UShort n_ins;
FT_Byte *flag, *flag_limit;
FT_Byte c, count;
FT_Vector *vec, *vec_limit;
FT_Pos x, y;
- FT_Short *cont, *cont_limit, prev_cont;
- FT_Int xy_size = 0;
+ FT_Short *cont, *cont_limit, last;
/* check that we can add the contours to the glyph */
@@ -380,41 +379,27 @@
if ( error )
goto Fail;
- /* reading the contours' endpoints & number of points */
- cont = gloader->current.outline.contours;
- cont_limit = cont + n_contours;
-
/* check space for contours array + instructions count */
- if ( n_contours >= 0xFFF || p + ( n_contours + 1 ) * 2 > limit )
+ if ( n_contours >= 0xFFF || p + 2 * n_contours + 2 > limit )
goto Invalid_Outline;
- prev_cont = FT_NEXT_SHORT( p );
-
- if ( n_contours > 0 )
- cont[0] = prev_cont;
-
- if ( prev_cont < 0 )
- goto Invalid_Outline;
+ /* reading the contours' endpoints & number of points */
+ cont = outline->contours;
+ cont_limit = cont + n_contours;
- for ( cont++; cont < cont_limit; cont++ )
+ last = -1;
+ for ( ; cont < cont_limit; cont++ )
{
- cont[0] = FT_NEXT_SHORT( p );
- if ( cont[0] <= prev_cont )
- {
- /* unordered contours: this is invalid */
- goto Invalid_Outline;
- }
- prev_cont = cont[0];
- }
+ *cont = FT_NEXT_SHORT( p );
- n_points = 0;
- if ( n_contours > 0 )
- {
- n_points = cont[-1] + 1;
- if ( n_points < 0 )
+ if ( *cont <= last )
goto Invalid_Outline;
+
+ last = *cont;
}
+ n_points = last + 1;
+
FT_TRACE5(( " # of points: %d\n", n_points ));
/* note that we will add four phantom points later */
@@ -422,59 +407,48 @@
if ( error )
goto Fail;
- /* reading the bytecode instructions */
- load->glyph->control_len = 0;
- load->glyph->control_data = NULL;
-
- if ( p + 2 > limit )
- goto Invalid_Outline;
-
+ /* space checked above */
n_ins = FT_NEXT_USHORT( p );
FT_TRACE5(( " Instructions size: %u\n", n_ins ));
+ /* check instructions size */
+ if ( p + n_ins > limit )
+ {
+ FT_TRACE1(( "TT_Load_Simple_Glyph: excessive instruction count\n" ));
+ error = FT_THROW( Too_Many_Hints );
+ goto Fail;
+ }
+
#ifdef TT_USE_BYTECODE_INTERPRETER
if ( IS_HINTED( load->load_flags ) )
{
- FT_ULong tmp;
+ TT_ExecContext exec = load->exec;
+ FT_Memory memory = exec->memory;
- /* check instructions size */
- if ( ( limit - p ) < n_ins )
- {
- FT_TRACE1(( "TT_Load_Simple_Glyph: instruction count mismatch\n" ));
- error = FT_THROW( Too_Many_Hints );
- goto Fail;
- }
+ if ( exec->glyphSize )
+ FT_FREE( exec->glyphIns );
+ exec->glyphSize = 0;
/* we don't trust `maxSizeOfInstructions' in the `maxp' table */
- /* and thus update the bytecode array size by ourselves */
-
- tmp = load->exec->glyphSize;
- error = Update_Max( load->exec->memory,
- &tmp,
- sizeof ( FT_Byte ),
- (void*)&load->exec->glyphIns,
- n_ins );
-
- load->exec->glyphSize = (FT_UInt)tmp;
- if ( error )
- return error;
+ /* and thus allocate the bytecode array size by ourselves */
+ if ( n_ins )
+ {
+ if ( FT_QNEW_ARRAY( exec->glyphIns, n_ins ) )
+ return error;
- load->glyph->control_len = n_ins;
- load->glyph->control_data = load->exec->glyphIns;
+ FT_MEM_COPY( exec->glyphIns, p, (FT_Long)n_ins );
- if ( n_ins )
- FT_MEM_COPY( load->exec->glyphIns, p, (FT_Long)n_ins );
+ exec->glyphSize = n_ins;
+ }
}
#endif /* TT_USE_BYTECODE_INTERPRETER */
p += n_ins;
- outline = &gloader->current.outline;
-
/* reading the point tags */
flag = (FT_Byte*)outline->tags;
flag_limit = flag + n_points;
@@ -512,9 +486,6 @@
flag = (FT_Byte*)outline->tags;
x = 0;
- if ( p + xy_size > limit )
- goto Invalid_Outline;
-
for ( ; vec < vec_limit; vec++, flag++ )
{
FT_Pos delta = 0;
@@ -544,7 +515,7 @@
/* reading the Y coordinates */
- vec = gloader->current.outline.points;
+ vec = outline->points;
vec_limit = vec + n_points;
flag = (FT_Byte*)outline->tags;
y = 0;
@@ -836,15 +807,14 @@
TT_GlyphZone zone = &loader->zone;
#ifdef TT_USE_BYTECODE_INTERPRETER
- FT_Long n_ins;
+ TT_ExecContext exec = loader->exec;
+ FT_Long n_ins = exec->glyphSize;
#else
FT_UNUSED( is_composite );
#endif
#ifdef TT_USE_BYTECODE_INTERPRETER
- n_ins = loader->glyph->control_len;
-
/* save original point positions in `org' array */
if ( n_ins > 0 )
FT_ARRAY_COPY( zone->org, zone->cur, zone->n_points );
@@ -856,15 +826,15 @@
/* completely refer to the (already) hinted subglyphs. */
if ( is_composite )
{
- loader->exec->metrics.x_scale = 1 << 16;
- loader->exec->metrics.y_scale = 1 << 16;
+ exec->metrics.x_scale = 1 << 16;
+ exec->metrics.y_scale = 1 << 16;
FT_ARRAY_COPY( zone->orus, zone->cur, zone->n_points );
}
else
{
- loader->exec->metrics.x_scale = loader->size->metrics->x_scale;
- loader->exec->metrics.y_scale = loader->size->metrics->y_scale;
+ exec->metrics.x_scale = loader->size->metrics->x_scale;
+ exec->metrics.y_scale = loader->size->metrics->y_scale;
}
#endif
@@ -884,23 +854,19 @@
{
FT_Error error;
- FT_GlyphLoader gloader = loader->gloader;
- FT_Outline current_outline = gloader->current.outline;
+ TT_Set_CodeRange( exec, tt_coderange_glyph, exec->glyphIns, n_ins );
- TT_Set_CodeRange( loader->exec, tt_coderange_glyph,
- loader->exec->glyphIns, n_ins );
-
- loader->exec->is_composite = is_composite;
- loader->exec->pts = *zone;
+ exec->is_composite = is_composite;
+ exec->pts = *zone;
error = TT_Run_Context( loader->exec );
- if ( error && loader->exec->pedantic_hinting )
+ if ( error && exec->pedantic_hinting )
return error;
/* store drop-out mode in bits 5-7; set bit 2 also as a marker */
- current_outline.tags[0] |=
- ( loader->exec->GS.scan_type << 5 ) | FT_CURVE_TAG_HAS_SCANMODE;
+ loader->gloader->current.outline.tags[0] |=
+ ( exec->GS.scan_type << 5 ) | FT_CURVE_TAG_HAS_SCANMODE;
}
#endif
@@ -910,7 +876,7 @@
/* compatibility mode, where no movement on the x axis means no reason */
/* to change bearings or advance widths. */
if ( !( driver->interpreter_version == TT_INTERPRETER_VERSION_40 &&
- loader->exec->backward_compatibility ) )
+ exec->backward_compatibility ) )
{
#endif
loader->pp1 = zone->cur[zone->n_points - 4];
@@ -924,10 +890,10 @@
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
if ( driver->interpreter_version == TT_INTERPRETER_VERSION_38 )
{
- if ( loader->exec->sph_tweak_flags & SPH_TWEAK_DEEMBOLDEN )
+ if ( exec->sph_tweak_flags & SPH_TWEAK_DEEMBOLDEN )
FT_Outline_EmboldenXY( &loader->gloader->current.outline, -24, 0 );
- else if ( loader->exec->sph_tweak_flags & SPH_TWEAK_EMBOLDEN )
+ else if ( exec->sph_tweak_flags & SPH_TWEAK_EMBOLDEN )
FT_Outline_EmboldenXY( &loader->gloader->current.outline, 24, 0 );
}
#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
@@ -949,10 +915,10 @@
static FT_Error
TT_Process_Simple_Glyph( TT_Loader loader )
{
- FT_GlyphLoader gloader = loader->gloader;
- FT_Error error = FT_Err_Ok;
- FT_Outline* outline;
- FT_Int n_points;
+ FT_Error error = FT_Err_Ok;
+ FT_GlyphLoader gloader = loader->gloader;
+ FT_Outline* outline = &gloader->current.outline;
+ FT_Int n_points = outline->n_points;
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
FT_Memory memory = loader->face->root.memory;
@@ -960,11 +926,7 @@
#endif
- outline = &gloader->current.outline;
- n_points = outline->n_points;
-
/* set phantom points */
-
outline->points[n_points ] = loader->pp1;
outline->points[n_points + 1] = loader->pp2;
outline->points[n_points + 2] = loader->pp3;
@@ -976,7 +938,7 @@
if ( !IS_DEFAULT_INSTANCE( FT_FACE( loader->face ) ) )
{
- if ( FT_NEW_ARRAY( unrounded, n_points ) )
+ if ( FT_QNEW_ARRAY( unrounded, n_points ) )
goto Exit;
/* Deltas apply to the unscaled data. */
@@ -1331,12 +1293,12 @@
FT_UInt start_contour )
{
FT_Error error;
- FT_Outline* outline;
+ FT_Outline* outline = &loader->gloader->base.outline;
+ FT_Stream stream = loader->stream;
+ FT_UShort n_ins;
FT_UInt i;
- outline = &loader->gloader->base.outline;
-
/* make room for phantom points */
error = FT_GLYPHLOADER_CHECK_POINTS( loader->gloader,
outline->n_points + 4,
@@ -1352,10 +1314,13 @@
#ifdef TT_USE_BYTECODE_INTERPRETER
{
- FT_Stream stream = loader->stream;
- FT_UShort n_ins, max_ins;
- FT_ULong tmp;
+ TT_ExecContext exec = loader->exec;
+ FT_Memory memory = exec->memory;
+
+ if ( exec->glyphSize )
+ FT_FREE( exec->glyphIns );
+ exec->glyphSize = 0;
/* TT_Load_Composite_Glyph only gives us the offset of instructions */
/* so we read them here */
@@ -1365,39 +1330,24 @@
FT_TRACE5(( " Instructions size = %hu\n", n_ins ));
- /* check it */
- max_ins = loader->face->max_profile.maxSizeOfInstructions;
- if ( n_ins > max_ins )
- {
- /* don't trust `maxSizeOfInstructions'; */
- /* only do a rough safety check */
- if ( n_ins > loader->byte_len )
- {
- FT_TRACE1(( "TT_Process_Composite_Glyph:"
- " too many instructions (%hu) for glyph with length %u\n",
- n_ins, loader->byte_len ));
- return FT_THROW( Too_Many_Hints );
- }
-
- tmp = loader->exec->glyphSize;
- error = Update_Max( loader->exec->memory,
- &tmp,
- sizeof ( FT_Byte ),
- (void*)&loader->exec->glyphIns,
- n_ins );
+ if ( !n_ins )
+ return FT_Err_Ok;
- loader->exec->glyphSize = (FT_UShort)tmp;
- if ( error )
- return error;
+ /* don't trust `maxSizeOfInstructions'; */
+ /* only do a rough safety check */
+ if ( n_ins > loader->byte_len )
+ {
+ FT_TRACE1(( "TT_Process_Composite_Glyph:"
+ " too many instructions (%hu) for glyph with length %u\n",
+ n_ins, loader->byte_len ));
+ return FT_THROW( Too_Many_Hints );
}
- else if ( n_ins == 0 )
- return FT_Err_Ok;
- if ( FT_STREAM_READ( loader->exec->glyphIns, n_ins ) )
+ if ( FT_QNEW_ARRAY( exec->glyphIns, n_ins ) ||
+ FT_STREAM_READ( exec->glyphIns, n_ins ) )
return error;
- loader->glyph->control_data = loader->exec->glyphIns;
- loader->glyph->control_len = n_ins;
+ exec->glyphSize = n_ins;
}
#endif
@@ -1662,8 +1612,14 @@
else
#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+ {
+ FT_ULong len;
+
- offset = tt_face_get_location( face, glyph_index, &loader->byte_len );
+ offset = tt_face_get_location( FT_FACE( face ), glyph_index, &len );
+
+ loader->byte_len = (FT_UInt)len;
+ }
if ( loader->byte_len > 0 )
{
@@ -1889,10 +1845,7 @@
short i, limit;
FT_SubGlyph subglyph;
- FT_Outline outline;
- FT_Vector* points = NULL;
- char* tags = NULL;
- short* contours = NULL;
+ FT_Outline outline = { 0, 0, NULL, NULL, NULL, 0 };
FT_Vector* unrounded = NULL;
@@ -1900,18 +1853,14 @@
/* construct an outline structure for */
/* communication with `TT_Vary_Apply_Glyph_Deltas' */
- outline.n_contours = outline.n_points = limit;
-
- outline.points = NULL;
- outline.tags = NULL;
- outline.contours = NULL;
-
- if ( FT_NEW_ARRAY( points, limit + 4 ) ||
- FT_NEW_ARRAY( tags, limit + 4 ) ||
- FT_NEW_ARRAY( contours, limit + 4 ) ||
- FT_NEW_ARRAY( unrounded, limit + 4 ) )
+ if ( FT_QNEW_ARRAY( outline.points, limit + 4 ) ||
+ FT_QNEW_ARRAY( outline.tags, limit ) ||
+ FT_QNEW_ARRAY( outline.contours, limit ) ||
+ FT_QNEW_ARRAY( unrounded, limit + 4 ) )
goto Exit1;
+ outline.n_contours = outline.n_points = limit;
+
subglyph = gloader->current.subglyphs;
for ( i = 0; i < limit; i++, subglyph++ )
@@ -1919,20 +1868,16 @@
/* applying deltas for anchor points doesn't make sense, */
/* but we don't have to specially check this since */
/* unused delta values are zero anyways */
- points[i].x = subglyph->arg1;
- points[i].y = subglyph->arg2;
- tags[i] = 1;
- contours[i] = i;
+ outline.points[i].x = subglyph->arg1;
+ outline.points[i].y = subglyph->arg2;
+ outline.tags[i] = ON_CURVE_POINT;
+ outline.contours[i] = i;
}
- points[i++] = loader->pp1;
- points[i++] = loader->pp2;
- points[i++] = loader->pp3;
- points[i ] = loader->pp4;
-
- outline.points = points;
- outline.tags = tags;
- outline.contours = contours;
+ outline.points[i++] = loader->pp1;
+ outline.points[i++] = loader->pp2;
+ outline.points[i++] = loader->pp3;
+ outline.points[i ] = loader->pp4;
/* this call provides additional offsets */
/* for each component's translation */
@@ -1947,8 +1892,8 @@
{
if ( subglyph->flags & ARGS_ARE_XY_VALUES )
{
- subglyph->arg1 = (FT_Int16)points[i].x;
- subglyph->arg2 = (FT_Int16)points[i].y;
+ subglyph->arg1 = (FT_Int16)outline.points[i].x;
+ subglyph->arg2 = (FT_Int16)outline.points[i].y;
}
}
@@ -2857,7 +2802,9 @@
#ifdef FT_CONFIG_OPTION_SVG
/* check for OT-SVG */
- if ( ( load_flags & FT_LOAD_COLOR ) && face->svg )
+ if ( ( load_flags & FT_LOAD_NO_SVG ) == 0 &&
+ ( load_flags & FT_LOAD_COLOR ) &&
+ face->svg )
{
SFNT_Service sfnt = (SFNT_Service)face->sfnt;
@@ -2955,6 +2902,9 @@
if ( IS_HINTED( load_flags ) )
{
+ glyph->control_data = loader.exec->glyphIns;
+ glyph->control_len = loader.exec->glyphSize;
+
if ( loader.exec->GS.scan_control )
{
/* convert scan conversion mode to FT_OUTLINE_XXX flags */
diff --git a/thirdparty/freetype/src/truetype/ttgxvar.c b/thirdparty/freetype/src/truetype/ttgxvar.c
index 60a0095b6e..8c713f1b6f 100644
--- a/thirdparty/freetype/src/truetype/ttgxvar.c
+++ b/thirdparty/freetype/src/truetype/ttgxvar.c
@@ -45,6 +45,7 @@
#include <freetype/internal/ftcalc.h>
#include <freetype/internal/ftstream.h>
#include <freetype/internal/sfnt.h>
+#include <freetype/internal/services/svmetric.h>
#include <freetype/tttags.h>
#include <freetype/ttnameid.h>
#include <freetype/ftmm.h>
@@ -465,7 +466,7 @@
if ( store_offset )
{
error = tt_var_load_item_variation_store(
- face,
+ FT_FACE( face ),
table_offset + store_offset,
&table->itemStore );
if ( error )
@@ -475,7 +476,7 @@
if ( axisMap_offset )
{
error = tt_var_load_delta_set_index_mapping(
- face,
+ FT_FACE( face ),
table_offset + axisMap_offset,
&table->axisMap,
&table->itemStore,
@@ -492,10 +493,11 @@
FT_LOCAL_DEF( FT_Error )
- tt_var_load_item_variation_store( TT_Face face,
+ tt_var_load_item_variation_store( FT_Face face, /* TT_Face */
FT_ULong offset,
GX_ItemVarStore itemStore )
{
+ TT_Face ttface = (TT_Face)face;
FT_Stream stream = FT_FACE_STREAM( face );
FT_Memory memory = stream->memory;
@@ -507,10 +509,10 @@
FT_UShort axis_count;
FT_UInt region_count;
- FT_UInt i, j, k;
+ FT_UInt i, j;
FT_Bool long_words;
- GX_Blend blend = face->blend;
+ GX_Blend blend = ttface->blend;
FT_ULong* dataOffsetArray = NULL;
@@ -622,6 +624,7 @@
FT_UInt item_count;
FT_UInt word_delta_count;
FT_UInt region_idx_count;
+ FT_UInt per_region_size;
if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) )
@@ -658,6 +661,8 @@
if ( FT_NEW_ARRAY( varData->regionIndices, region_idx_count ) )
goto Exit;
varData->regionIdxCount = region_idx_count;
+ varData->wordDeltaCount = word_delta_count;
+ varData->longWords = long_words;
for ( j = 0; j < varData->regionIdxCount; j++ )
{
@@ -673,37 +678,22 @@
}
}
- /* Parse delta set. */
- /* */
- /* On input, deltas are (word_delta_count + region_idx_count) bytes */
- /* each if `long_words` isn't set, and twice as much otherwise. */
- /* */
- /* On output, deltas are expanded to `region_idx_count` shorts each. */
- if ( FT_NEW_ARRAY( varData->deltaSet, item_count * region_idx_count ) )
- goto Exit;
- varData->itemCount = item_count;
+ per_region_size = word_delta_count + region_idx_count;
+ if ( long_words )
+ per_region_size *= 2;
- for ( j = 0; j < item_count * region_idx_count; )
+ if ( FT_NEW_ARRAY( varData->deltaSet, per_region_size * item_count ) )
+ goto Exit;
+ if ( FT_Stream_Read( stream,
+ varData->deltaSet,
+ per_region_size * item_count ) )
{
- if ( long_words )
- {
- for ( k = 0; k < word_delta_count; k++, j++ )
- if ( FT_READ_LONG( varData->deltaSet[j] ) )
- goto Exit;
- for ( ; k < region_idx_count; k++, j++ )
- if ( FT_READ_SHORT( varData->deltaSet[j] ) )
- goto Exit;
- }
- else
- {
- for ( k = 0; k < word_delta_count; k++, j++ )
- if ( FT_READ_SHORT( varData->deltaSet[j] ) )
- goto Exit;
- for ( ; k < region_idx_count; k++, j++ )
- if ( FT_READ_CHAR( varData->deltaSet[j] ) )
- goto Exit;
- }
+ FT_TRACE2(( "deltaSet read failed." ));
+ error = FT_THROW( Invalid_Table );
+ goto Exit;
}
+
+ varData->itemCount = item_count;
}
Exit:
@@ -714,7 +704,7 @@
FT_LOCAL_DEF( FT_Error )
- tt_var_load_delta_set_index_mapping( TT_Face face,
+ tt_var_load_delta_set_index_mapping( FT_Face face, /* TT_Face */
FT_ULong offset,
GX_DeltaSetIdxMap map,
GX_ItemVarStore itemStore,
@@ -941,7 +931,7 @@
}
error = tt_var_load_item_variation_store(
- face,
+ FT_FACE( face ),
table_offset + store_offset,
&table->itemStore );
if ( error )
@@ -950,7 +940,7 @@
if ( widthMap_offset )
{
error = tt_var_load_delta_set_index_mapping(
- face,
+ FT_FACE( face ),
table_offset + widthMap_offset,
&table->widthMap,
&table->itemStore,
@@ -992,24 +982,30 @@
FT_LOCAL_DEF( FT_ItemVarDelta )
- tt_var_get_item_delta( TT_Face face,
+ tt_var_get_item_delta( FT_Face face, /* TT_Face */
GX_ItemVarStore itemStore,
FT_UInt outerIndex,
FT_UInt innerIndex )
{
+ TT_Face ttface = (TT_Face)face;
FT_Stream stream = FT_FACE_STREAM( face );
FT_Memory memory = stream->memory;
FT_Error error = FT_Err_Ok;
GX_ItemVarData varData;
- FT_ItemVarDelta* deltaSet;
+ FT_ItemVarDelta* deltaSet = NULL;
+ FT_ItemVarDelta deltaSetStack[16];
+
+ FT_Fixed* scalars = NULL;
+ FT_Fixed scalarsStack[16];
FT_UInt master, j;
- FT_Fixed* scalars = NULL;
- FT_ItemVarDelta returnValue;
+ FT_ItemVarDelta returnValue = 0;
+ FT_UInt per_region_size;
+ FT_Byte* bytes;
- if ( !face->blend || !face->blend->normalizedcoords )
+ if ( !ttface->blend || !ttface->blend->normalizedcoords )
return 0;
/* OpenType 1.8.4+: No variation data for this item */
@@ -1023,15 +1019,48 @@
if ( outerIndex >= itemStore->dataCount )
return 0; /* Out of range. */
- varData = &itemStore->varData[outerIndex];
- deltaSet = FT_OFFSET( varData->deltaSet,
- varData->regionIdxCount * innerIndex );
+ varData = &itemStore->varData[outerIndex];
if ( innerIndex >= varData->itemCount )
return 0; /* Out of range. */
- if ( FT_QNEW_ARRAY( scalars, varData->regionIdxCount ) )
- return 0;
+ if ( varData->regionIdxCount < 16 )
+ {
+ deltaSet = deltaSetStack;
+ scalars = scalarsStack;
+ }
+ else
+ {
+ if ( FT_QNEW_ARRAY( deltaSet, varData->regionIdxCount ) )
+ goto Exit;
+ if ( FT_QNEW_ARRAY( scalars, varData->regionIdxCount ) )
+ goto Exit;
+ }
+
+ /* Parse delta set. */
+ /* */
+ /* Deltas are (word_delta_count + region_idx_count) bytes each */
+ /* if `longWords` isn't set, and twice as much otherwise. */
+ per_region_size = varData->wordDeltaCount + varData->regionIdxCount;
+ if ( varData->longWords )
+ per_region_size *= 2;
+
+ bytes = varData->deltaSet + per_region_size * innerIndex;
+
+ if ( varData->longWords )
+ {
+ for ( master = 0; master < varData->wordDeltaCount; master++ )
+ deltaSet[master] = FT_NEXT_LONG( bytes );
+ for ( ; master < varData->regionIdxCount; master++ )
+ deltaSet[master] = FT_NEXT_SHORT( bytes );
+ }
+ else
+ {
+ for ( master = 0; master < varData->wordDeltaCount; master++ )
+ deltaSet[master] = FT_NEXT_SHORT( bytes );
+ for ( ; master < varData->regionIdxCount; master++ )
+ deltaSet[master] = FT_NEXT_CHAR( bytes );
+ }
/* outer loop steps through master designs to be blended */
for ( master = 0; master < varData->regionIdxCount; master++ )
@@ -1060,27 +1089,27 @@
else if ( axis->peakCoord == 0 )
continue;
- else if ( face->blend->normalizedcoords[j] == axis->peakCoord )
+ else if ( ttface->blend->normalizedcoords[j] == axis->peakCoord )
continue;
/* ignore this region if coords are out of range */
- else if ( face->blend->normalizedcoords[j] <= axis->startCoord ||
- face->blend->normalizedcoords[j] >= axis->endCoord )
+ else if ( ttface->blend->normalizedcoords[j] <= axis->startCoord ||
+ ttface->blend->normalizedcoords[j] >= axis->endCoord )
{
scalar = 0;
break;
}
/* cumulative product of all the axis scalars */
- else if ( face->blend->normalizedcoords[j] < axis->peakCoord )
+ else if ( ttface->blend->normalizedcoords[j] < axis->peakCoord )
scalar =
FT_MulDiv( scalar,
- face->blend->normalizedcoords[j] - axis->startCoord,
+ ttface->blend->normalizedcoords[j] - axis->startCoord,
axis->peakCoord - axis->startCoord );
else
scalar =
FT_MulDiv( scalar,
- axis->endCoord - face->blend->normalizedcoords[j],
+ axis->endCoord - ttface->blend->normalizedcoords[j],
axis->endCoord - axis->peakCoord );
} /* per-axis loop */
@@ -1106,7 +1135,11 @@
*/
returnValue = FT_MulAddFix( scalars, deltaSet, varData->regionIdxCount );
- FT_FREE( scalars );
+ Exit:
+ if ( scalars != scalarsStack )
+ FT_FREE( scalars );
+ if ( deltaSet != deltaSetStack )
+ FT_FREE( deltaSet );
return returnValue;
}
@@ -1206,7 +1239,7 @@
innerIndex = gindex;
}
- delta = tt_var_get_item_delta( face,
+ delta = tt_var_get_item_delta( FT_FACE( face ),
&table->itemStore,
outerIndex,
innerIndex );
@@ -1229,20 +1262,20 @@
FT_LOCAL_DEF( FT_Error )
- tt_hadvance_adjust( TT_Face face,
+ tt_hadvance_adjust( FT_Face face, /* TT_Face */
FT_UInt gindex,
FT_Int *avalue )
{
- return tt_hvadvance_adjust( face, gindex, avalue, 0 );
+ return tt_hvadvance_adjust( (TT_Face)face, gindex, avalue, 0 );
}
FT_LOCAL_DEF( FT_Error )
- tt_vadvance_adjust( TT_Face face,
+ tt_vadvance_adjust( FT_Face face, /* TT_Face */
FT_UInt gindex,
FT_Int *avalue )
{
- return tt_hvadvance_adjust( face, gindex, avalue, 1 );
+ return tt_hvadvance_adjust( (TT_Face)face, gindex, avalue, 1 );
}
@@ -1389,7 +1422,7 @@
records_offset = FT_STREAM_POS();
error = tt_var_load_item_variation_store(
- face,
+ FT_FACE( face ),
table_offset + store_offset,
&blend->mvar_table->itemStore );
if ( error )
@@ -1462,15 +1495,14 @@
static FT_Error
- tt_size_reset_iterator( FT_ListNode node,
+ ft_size_reset_iterator( FT_ListNode node,
void* user )
{
- TT_Size size = (TT_Size)node->data;
-
- FT_UNUSED( user );
+ FT_Size size = (FT_Size)node->data;
+ FT_Service_MetricsVariations var = (FT_Service_MetricsVariations)user;
- tt_size_reset( size, 1 );
+ var->size_reset( size );
return FT_Err_Ok;
}
@@ -1489,16 +1521,19 @@
* The font face.
*/
FT_LOCAL_DEF( void )
- tt_apply_mvar( TT_Face face )
+ tt_apply_mvar( FT_Face face ) /* TT_Face */
{
- GX_Blend blend = face->blend;
+ TT_Face ttface = (TT_Face)face;
+
+ GX_Blend blend = ttface->blend;
GX_Value value, limit;
+
FT_Short mvar_hasc_delta = 0;
FT_Short mvar_hdsc_delta = 0;
FT_Short mvar_hlgp_delta = 0;
- if ( !( face->variation_support & TT_FACE_FLAG_VAR_MVAR ) )
+ if ( !( ttface->variation_support & TT_FACE_FLAG_VAR_MVAR ) )
return;
value = blend->mvar_table->values;
@@ -1506,7 +1541,7 @@
for ( ; value < limit; value++ )
{
- FT_Short* p = ft_var_get_value_pointer( face, value->tag );
+ FT_Short* p = ft_var_get_value_pointer( ttface, value->tag );
FT_Int delta;
@@ -1543,7 +1578,8 @@
/* adjust all derived values */
{
- FT_Face root = &face->root;
+ FT_Service_MetricsVariations var =
+ (FT_Service_MetricsVariations)ttface->face_var;
/*
* Apply the deltas of hasc, hdsc and hlgp to the FT_Face's ascender,
@@ -1571,24 +1607,25 @@
* whether they were actually changed or the font had the OS/2 table's
* fsSelection's bit 7 (USE_TYPO_METRICS) set.
*/
- FT_Short current_line_gap = root->height - root->ascender +
- root->descender;
+ FT_Short current_line_gap = face->height - face->ascender +
+ face->descender;
- root->ascender = root->ascender + mvar_hasc_delta;
- root->descender = root->descender + mvar_hdsc_delta;
- root->height = root->ascender - root->descender +
+ face->ascender = face->ascender + mvar_hasc_delta;
+ face->descender = face->descender + mvar_hdsc_delta;
+ face->height = face->ascender - face->descender +
current_line_gap + mvar_hlgp_delta;
- root->underline_position = face->postscript.underlinePosition -
- face->postscript.underlineThickness / 2;
- root->underline_thickness = face->postscript.underlineThickness;
+ face->underline_position = ttface->postscript.underlinePosition -
+ ttface->postscript.underlineThickness / 2;
+ face->underline_thickness = ttface->postscript.underlineThickness;
- /* iterate over all FT_Size objects and call `tt_size_reset' */
- /* to propagate the metrics changes */
- FT_List_Iterate( &root->sizes_list,
- tt_size_reset_iterator,
- NULL );
+ /* iterate over all FT_Size objects and call `var->size_reset' */
+ /* to propagate the metrics changes */
+ if ( var && var->size_reset )
+ FT_List_Iterate( &face->sizes_list,
+ ft_size_reset_iterator,
+ (void*)var );
}
}
@@ -2099,7 +2136,7 @@
innerIndex = table->axisMap.innerIndex[idx];
}
- delta = tt_var_get_item_delta( face,
+ delta = tt_var_get_item_delta( FT_FACE( face ),
&table->itemStore,
outerIndex,
innerIndex );
@@ -2261,11 +2298,12 @@
* FreeType error code. 0 means success.
*/
FT_LOCAL_DEF( FT_Error )
- TT_Get_MM_Var( TT_Face face,
+ TT_Get_MM_Var( FT_Face face, /* TT_Face */
FT_MM_Var* *master )
{
- FT_Stream stream = face->root.stream;
- FT_Memory memory = face->root.memory;
+ TT_Face ttface = (TT_Face)face;
+ FT_Stream stream = FT_FACE_STREAM( face );
+ FT_Memory memory = FT_FACE_MEMORY( face );
FT_ULong table_len;
FT_Error error = FT_Err_Ok;
FT_ULong fvar_start = 0;
@@ -2329,19 +2367,19 @@
/* the default instance, which might be missing in the table of named */
/* instances (in 'fvar'). This value is validated in `sfobjs.c` and */
/* may be reset to 0 if consistency checks fail. */
- num_instances = (FT_UInt)face->root.style_flags >> 16;
+ num_instances = (FT_UInt)face->style_flags >> 16;
/* read the font data and set up the internal representation */
/* if not already done */
- need_init = !face->blend;
+ need_init = !ttface->blend;
if ( need_init )
{
FT_TRACE2(( "FVAR " ));
- if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar,
- stream, &table_len ) ) )
+ if ( FT_SET_ERROR( ttface->goto_table( ttface, TTAG_fvar,
+ stream, &table_len ) ) )
{
FT_TRACE1(( "is missing\n" ));
goto Exit;
@@ -2374,14 +2412,14 @@
fvar_head.axisCount,
fvar_head.axisCount == 1 ? "is" : "es" ));
- if ( FT_NEW( face->blend ) )
+ if ( FT_NEW( ttface->blend ) )
goto Exit;
- num_axes = fvar_head.axisCount;
- face->blend->num_axis = num_axes;
+ num_axes = fvar_head.axisCount;
+ ttface->blend->num_axis = num_axes;
}
else
- num_axes = face->blend->num_axis;
+ num_axes = ttface->blend->num_axis;
/* prepare storage area for MM data; this cannot overflow */
/* 32-bit arithmetic because of the size limits used in the */
@@ -2410,16 +2448,16 @@
if ( need_init )
{
- face->blend->mmvar_len = mmvar_size +
- axis_flags_size +
- axis_size +
- namedstyle_size +
- next_coords_size +
- next_name_size;
-
- if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
+ ttface->blend->mmvar_len = mmvar_size +
+ axis_flags_size +
+ axis_size +
+ namedstyle_size +
+ next_coords_size +
+ next_name_size;
+
+ if ( FT_ALLOC( mmvar, ttface->blend->mmvar_len ) )
goto Exit;
- face->blend->mmvar = mmvar;
+ ttface->blend->mmvar = mmvar;
/* set up pointers and offsets into the `mmvar' array; */
/* the data gets filled in later on */
@@ -2525,27 +2563,27 @@
/* named instance coordinates are stored as design coordinates; */
/* we have to convert them to normalized coordinates also */
- if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
+ if ( FT_NEW_ARRAY( ttface->blend->normalized_stylecoords,
num_axes * num_instances ) )
goto Exit;
- if ( fvar_head.instanceCount && !face->blend->avar_loaded )
+ if ( fvar_head.instanceCount && !ttface->blend->avar_loaded )
{
FT_ULong offset = FT_STREAM_POS();
- ft_var_load_avar( face );
+ ft_var_load_avar( ttface );
if ( FT_STREAM_SEEK( offset ) )
goto Exit;
}
- FT_TRACE5(( "%d instance%s\n",
+ FT_TRACE5(( "%d named instance%s\n",
fvar_head.instanceCount,
fvar_head.instanceCount == 1 ? "" : "s" ));
ns = mmvar->namedstyle;
- nsc = face->blend->normalized_stylecoords;
+ nsc = ttface->blend->normalized_stylecoords;
for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
{
/* PostScript names add 2 bytes to the instance record size */
@@ -2568,7 +2606,7 @@
#ifdef FT_DEBUG_LEVEL_TRACE
{
- SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+ SFNT_Service sfnt = (SFNT_Service)ttface->sfnt;
FT_String* strname = NULL;
FT_String* psname = NULL;
@@ -2580,7 +2618,7 @@
if ( ns->strid != 0xFFFF )
{
- (void)sfnt->get_name( face,
+ (void)sfnt->get_name( ttface,
(FT_UShort)ns->strid,
&strname );
if ( strname && !ft_strcmp( strname, ".notdef" ) )
@@ -2589,7 +2627,7 @@
if ( ns->psid != 0xFFFF )
{
- (void)sfnt->get_name( face,
+ (void)sfnt->get_name( ttface,
(FT_UShort)ns->psid,
&psname );
if ( psname && !ft_strcmp( psname, ".notdef" ) )
@@ -2598,7 +2636,7 @@
(void)FT_STREAM_SEEK( pos );
- FT_TRACE5(( " instance %d (%s%s%s, %s%s%s)\n",
+ FT_TRACE5(( " named instance %d (%s%s%s, %s%s%s)\n",
i,
strname ? "name: `" : "",
strname ? strname : "unnamed",
@@ -2612,7 +2650,7 @@
}
#endif /* FT_DEBUG_LEVEL_TRACE */
- ft_var_to_normalized( face, num_axes, ns->coords, nsc );
+ ft_var_to_normalized( ttface, num_axes, ns->coords, nsc );
nsc += num_axes;
FT_FRAME_EXIT();
@@ -2620,15 +2658,17 @@
if ( num_instances != fvar_head.instanceCount )
{
- SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+ SFNT_Service sfnt = (SFNT_Service)ttface->sfnt;
FT_Int found, dummy1, dummy2;
FT_UInt strid = ~0U;
- /* the default instance is missing in array the */
- /* of named instances; try to synthesize an entry */
- found = sfnt->get_name_id( face,
+ /* The default instance is missing in array the */
+ /* of named instances; try to synthesize an entry. */
+ /* If this fails, `default_named_instance` remains */
+ /* at value zero, which doesn't do any harm. */
+ found = sfnt->get_name_id( ttface,
TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,
&dummy1,
&dummy2 );
@@ -2636,7 +2676,7 @@
strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY;
else
{
- found = sfnt->get_name_id( face,
+ found = sfnt->get_name_id( ttface,
TT_NAME_ID_FONT_SUBFAMILY,
&dummy1,
&dummy2 );
@@ -2646,7 +2686,7 @@
if ( found )
{
- found = sfnt->get_name_id( face,
+ found = sfnt->get_name_id( ttface,
TT_NAME_ID_PS_NAME,
&dummy1,
&dummy2 );
@@ -2655,6 +2695,9 @@
FT_TRACE5(( "TT_Get_MM_Var:"
" Adding default instance to named instances\n" ));
+ /* named instance indices start with value 1 */
+ ttface->var_default_named_instance = num_instances;
+
ns = &mmvar->namedstyle[fvar_head.instanceCount];
ns->strid = strid;
@@ -2668,7 +2711,7 @@
}
}
- ft_var_load_mvar( face );
+ ft_var_load_mvar( ttface );
}
/* fill the output array if requested */
@@ -2678,9 +2721,9 @@
FT_UInt n;
- if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
+ if ( FT_ALLOC( mmvar, ttface->blend->mmvar_len ) )
goto Exit;
- FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
+ FT_MEM_COPY( mmvar, ttface->blend->mmvar, ttface->blend->mmvar_len );
axis_flags =
(FT_UShort*)( (char*)mmvar + mmvar_size );
@@ -2756,7 +2799,7 @@
if ( !face->blend )
{
- if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+ if ( FT_SET_ERROR( TT_Get_MM_Var( FT_FACE( face ), NULL ) ) )
goto Exit;
}
@@ -2841,26 +2884,29 @@
}
}
- if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
+ if ( !have_diff )
{
- FT_UInt instance_index = (FT_UInt)face->root.face_index >> 16;
+ if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
+ {
+ FT_UInt instance_index = (FT_UInt)face->root.face_index >> 16;
- c = blend->normalizedcoords + i;
- n = blend->normalized_stylecoords +
- ( instance_index - 1 ) * mmvar->num_axis +
- i;
+ c = blend->normalizedcoords + i;
+ n = blend->normalized_stylecoords +
+ ( instance_index - 1 ) * mmvar->num_axis +
+ i;
- for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
- if ( *c != *n )
- have_diff = 1;
- }
- else
- {
- c = blend->normalizedcoords + i;
- for ( j = i; j < mmvar->num_axis; j++, c++ )
- if ( *c != 0 )
- have_diff = 1;
+ for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
+ if ( *c != *n )
+ have_diff = 1;
+ }
+ else
+ {
+ c = blend->normalizedcoords + i;
+ for ( j = i; j < mmvar->num_axis; j++, c++ )
+ if ( *c != 0 )
+ have_diff = 1;
+ }
}
/* return value -1 indicates `no change' */
@@ -2924,9 +2970,6 @@
}
}
- /* enforce recomputation of the PostScript name; */
- FT_FREE( face->postscript_name );
-
Exit:
return error;
}
@@ -2958,26 +3001,15 @@
* An array of `num_coords', each between [-1,1].
*
* @Return:
- * FreeType error code. 0 means success.
+ * FreeType error code. 0 means success, -1 means success and unchanged
+ * axis values.
*/
FT_LOCAL_DEF( FT_Error )
- TT_Set_MM_Blend( TT_Face face,
+ TT_Set_MM_Blend( FT_Face face, /* TT_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
- FT_Error error;
-
-
- error = tt_set_mm_blend( face, num_coords, coords, 1 );
- if ( error )
- return error;
-
- if ( num_coords )
- face->root.face_flags |= FT_FACE_FLAG_VARIATION;
- else
- face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
-
- return FT_Err_Ok;
+ return tt_set_mm_blend( (TT_Face)face, num_coords, coords, 1 );
}
@@ -3005,31 +3037,34 @@
* An array of `num_coords', each between [-1,1].
*
* @Return:
- * FreeType error code. 0 means success.
+ * FreeType error code. 0 means success, -1 means success and unchanged
+ * axis values.
*/
FT_LOCAL_DEF( FT_Error )
- TT_Get_MM_Blend( TT_Face face,
+ TT_Get_MM_Blend( FT_Face face, /* TT_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
+ TT_Face ttface = (TT_Face)face;
+
FT_Error error = FT_Err_Ok;
GX_Blend blend;
FT_UInt i, nc;
- if ( !face->blend )
+ if ( !ttface->blend )
{
if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
return error;
}
- blend = face->blend;
+ blend = ttface->blend;
if ( !blend->coords )
{
/* select default instance coordinates */
/* if no instance is selected yet */
- if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
+ if ( FT_SET_ERROR( tt_set_mm_blend( ttface, 0, NULL, 1 ) ) )
return error;
}
@@ -3042,7 +3077,7 @@
nc = blend->num_axis;
}
- if ( face->doblend )
+ if ( ttface->doblend )
{
for ( i = 0; i < nc; i++ )
coords[i] = blend->normalizedcoords[i];
@@ -3089,15 +3124,16 @@
* FreeType error code. 0 means success.
*/
FT_LOCAL_DEF( FT_Error )
- TT_Set_Var_Design( TT_Face face,
+ TT_Set_Var_Design( FT_Face face, /* TT_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
+ TT_Face ttface = (TT_Face)face;
FT_Error error = FT_Err_Ok;
GX_Blend blend;
FT_MM_Var* mmvar;
FT_UInt i;
- FT_Memory memory = face->root.memory;
+ FT_Memory memory = FT_FACE_MEMORY( face );
FT_Fixed* c;
FT_Fixed* n;
@@ -3106,13 +3142,13 @@
FT_Bool have_diff = 0;
- if ( !face->blend )
+ if ( !ttface->blend )
{
if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
goto Exit;
}
- blend = face->blend;
+ blend = ttface->blend;
mmvar = blend->mmvar;
if ( num_coords > mmvar->num_axis )
@@ -3140,13 +3176,13 @@
}
}
- if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
+ if ( FT_IS_NAMED_INSTANCE( face ) )
{
FT_UInt instance_index;
FT_Var_Named_Style* named_style;
- instance_index = (FT_UInt)face->root.face_index >> 16;
+ instance_index = (FT_UInt)face->face_index >> 16;
named_style = mmvar->namedstyle + instance_index - 1;
n = named_style->coords + num_coords;
@@ -3183,22 +3219,17 @@
if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
goto Exit;
- if ( !face->blend->avar_loaded )
- ft_var_load_avar( face );
+ if ( !ttface->blend->avar_loaded )
+ ft_var_load_avar( ttface );
FT_TRACE5(( "TT_Set_Var_Design:\n" ));
FT_TRACE5(( " normalized design coordinates:\n" ));
- ft_var_to_normalized( face, num_coords, blend->coords, normalized );
+ ft_var_to_normalized( ttface, num_coords, blend->coords, normalized );
- error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
+ error = tt_set_mm_blend( ttface, mmvar->num_axis, normalized, 0 );
if ( error )
goto Exit;
- if ( num_coords )
- face->root.face_flags |= FT_FACE_FLAG_VARIATION;
- else
- face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
-
Exit:
FT_FREE( normalized );
return error;
@@ -3231,28 +3262,29 @@
* FreeType error code. 0~means success.
*/
FT_LOCAL_DEF( FT_Error )
- TT_Get_Var_Design( TT_Face face,
+ TT_Get_Var_Design( FT_Face face, /* TT_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
- FT_Error error = FT_Err_Ok;
+ TT_Face ttface = (TT_Face)face;
+ FT_Error error = FT_Err_Ok;
GX_Blend blend;
FT_UInt i, nc;
- if ( !face->blend )
+ if ( !ttface->blend )
{
if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
return error;
}
- blend = face->blend;
+ blend = ttface->blend;
if ( !blend->coords )
{
/* select default instance coordinates */
/* if no instance is selected yet */
- if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
+ if ( FT_SET_ERROR( tt_set_mm_blend( ttface, 0, NULL, 1 ) ) )
return error;
}
@@ -3265,7 +3297,7 @@
nc = blend->num_axis;
}
- if ( face->doblend )
+ if ( ttface->doblend )
{
for ( i = 0; i < nc; i++ )
coords[i] = blend->coords[i];
@@ -3301,29 +3333,33 @@
* Value 0 indicates to not use an instance.
*
* @Return:
- * FreeType error code. 0~means success.
+ * FreeType error code. 0~means success, -1 means success and unchanged
+ * axis values.
*/
FT_LOCAL_DEF( FT_Error )
- TT_Set_Named_Instance( TT_Face face,
+ TT_Set_Named_Instance( FT_Face face, /* TT_Face */
FT_UInt instance_index )
{
+ TT_Face ttface = (TT_Face)face;
FT_Error error;
GX_Blend blend;
FT_MM_Var* mmvar;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
FT_UInt num_instances;
- if ( !face->blend )
+ if ( !ttface->blend )
{
if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
goto Exit;
}
- blend = face->blend;
+ blend = ttface->blend;
mmvar = blend->mmvar;
- num_instances = (FT_UInt)face->root.style_flags >> 16;
+ num_instances = (FT_UInt)face->style_flags >> 16;
/* `instance_index' starts with value 1, thus `>' */
if ( instance_index > num_instances )
@@ -3334,8 +3370,7 @@
if ( instance_index > 0 )
{
- FT_Memory memory = face->root.memory;
- SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+ SFNT_Service sfnt = (SFNT_Service)ttface->sfnt;
FT_Var_Named_Style* named_style;
FT_String* style_name;
@@ -3343,40 +3378,89 @@
named_style = mmvar->namedstyle + instance_index - 1;
- error = sfnt->get_name( face,
+ error = sfnt->get_name( ttface,
(FT_UShort)named_style->strid,
&style_name );
if ( error )
goto Exit;
/* set (or replace) style name */
- FT_FREE( face->root.style_name );
- face->root.style_name = style_name;
+ FT_FREE( face->style_name );
+ face->style_name = style_name;
/* finally, select the named instance */
error = TT_Set_Var_Design( face,
mmvar->num_axis,
named_style->coords );
- if ( error )
- {
- /* internal error code -1 means `no change' */
- if ( error == -1 )
- error = FT_Err_Ok;
- goto Exit;
- }
}
else
+ {
+ /* restore non-VF style name */
+ FT_FREE( face->style_name );
+ if ( FT_STRDUP( face->style_name, ttface->non_var_style_name ) )
+ goto Exit;
error = TT_Set_Var_Design( face, 0, NULL );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * TT_Get_Default_Named_Instance
+ *
+ * @Description:
+ * Get the default named instance.
+ *
+ * @Input:
+ * face ::
+ * A handle to the source face.
+ *
+ * @Output:
+ * instance_index ::
+ * The default named instance index.
+ *
+ * @Return:
+ * FreeType error code. 0~means success.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Get_Default_Named_Instance( FT_Face face,
+ FT_UInt *instance_index )
+ {
+ TT_Face ttface = (TT_Face)face;
+ FT_Error error = FT_Err_Ok;
- face->root.face_index = ( instance_index << 16 ) |
- ( face->root.face_index & 0xFFFFL );
- face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
+
+ if ( !ttface->blend )
+ {
+ if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+ goto Exit;
+ }
+
+ *instance_index = ttface->var_default_named_instance;
Exit:
return error;
}
+ /* This function triggers (lazy) recomputation of the `postscript_name` */
+ /* field in `TT_Face`. */
+
+ FT_LOCAL_DEF( void )
+ tt_construct_ps_name( FT_Face face )
+ {
+ TT_Face ttface = (TT_Face)face;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( ttface->postscript_name );
+ }
+
+
/*************************************************************************/
/*************************************************************************/
/***** *****/
@@ -4409,22 +4493,25 @@
* the MM machinery in case it isn't loaded yet.
*/
FT_LOCAL_DEF( FT_Error )
- tt_get_var_blend( TT_Face face,
+ tt_get_var_blend( FT_Face face, /* TT_Face */
FT_UInt *num_coords,
FT_Fixed* *coords,
FT_Fixed* *normalizedcoords,
FT_MM_Var* *mm_var )
{
- if ( face->blend )
+ TT_Face ttface = (TT_Face)face;
+
+
+ if ( ttface->blend )
{
if ( num_coords )
- *num_coords = face->blend->num_axis;
+ *num_coords = ttface->blend->num_axis;
if ( coords )
- *coords = face->blend->coords;
+ *coords = ttface->blend->coords;
if ( normalizedcoords )
- *normalizedcoords = face->blend->normalizedcoords;
+ *normalizedcoords = ttface->blend->normalizedcoords;
if ( mm_var )
- *mm_var = face->blend->mmvar;
+ *mm_var = ttface->blend->mmvar;
}
else
{
@@ -4441,7 +4528,7 @@
FT_LOCAL_DEF( void )
- tt_var_done_item_variation_store( TT_Face face,
+ tt_var_done_item_variation_store( FT_Face face,
GX_ItemVarStore itemStore )
{
FT_Memory memory = FT_FACE_MEMORY( face );
@@ -4470,7 +4557,7 @@
FT_LOCAL_DEF( void )
- tt_var_done_delta_set_index_map( TT_Face face,
+ tt_var_done_delta_set_index_map( FT_Face face,
GX_DeltaSetIdxMap deltaSetIdxMap )
{
FT_Memory memory = FT_FACE_MEMORY( face );
@@ -4490,10 +4577,11 @@
* Free the blend internal data structure.
*/
FT_LOCAL_DEF( void )
- tt_done_blend( TT_Face face )
+ tt_done_blend( FT_Face face )
{
+ TT_Face ttface = (TT_Face)face;
FT_Memory memory = FT_FACE_MEMORY( face );
- GX_Blend blend = face->blend;
+ GX_Blend blend = ttface->blend;
if ( blend )
@@ -4565,7 +4653,7 @@
#else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
/* ANSI C doesn't like empty source files */
- typedef int _tt_gxvar_dummy;
+ typedef int tt_gxvar_dummy_;
#endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
diff --git a/thirdparty/freetype/src/truetype/ttgxvar.h b/thirdparty/freetype/src/truetype/ttgxvar.h
index 4fec980dcc..e3da6d1705 100644
--- a/thirdparty/freetype/src/truetype/ttgxvar.h
+++ b/thirdparty/freetype/src/truetype/ttgxvar.h
@@ -347,34 +347,41 @@ FT_BEGIN_HEADER
FT_LOCAL( FT_Error )
- TT_Set_MM_Blend( TT_Face face,
+ TT_Set_MM_Blend( FT_Face face,
FT_UInt num_coords,
FT_Fixed* coords );
FT_LOCAL( FT_Error )
- TT_Get_MM_Blend( TT_Face face,
+ TT_Get_MM_Blend( FT_Face face,
FT_UInt num_coords,
FT_Fixed* coords );
FT_LOCAL( FT_Error )
- TT_Set_Var_Design( TT_Face face,
+ TT_Set_Var_Design( FT_Face face,
FT_UInt num_coords,
FT_Fixed* coords );
FT_LOCAL( FT_Error )
- TT_Get_MM_Var( TT_Face face,
+ TT_Get_MM_Var( FT_Face face,
FT_MM_Var* *master );
FT_LOCAL( FT_Error )
- TT_Get_Var_Design( TT_Face face,
+ TT_Get_Var_Design( FT_Face face,
FT_UInt num_coords,
FT_Fixed* coords );
FT_LOCAL( FT_Error )
- TT_Set_Named_Instance( TT_Face face,
+ TT_Set_Named_Instance( FT_Face face,
FT_UInt instance_index );
FT_LOCAL( FT_Error )
+ TT_Get_Default_Named_Instance( FT_Face face,
+ FT_UInt *instance_index );
+
+ FT_LOCAL( void )
+ tt_construct_ps_name( FT_Face face );
+
+ FT_LOCAL( FT_Error )
tt_face_vary_cvt( TT_Face face,
FT_Stream stream );
@@ -385,55 +392,54 @@ FT_BEGIN_HEADER
FT_Vector* unrounded );
FT_LOCAL( FT_Error )
- tt_hadvance_adjust( TT_Face face,
+ tt_hadvance_adjust( FT_Face face,
FT_UInt gindex,
FT_Int *adelta );
FT_LOCAL( FT_Error )
- tt_vadvance_adjust( TT_Face face,
+ tt_vadvance_adjust( FT_Face face,
FT_UInt gindex,
FT_Int *adelta );
FT_LOCAL( void )
- tt_apply_mvar( TT_Face face );
-
+ tt_apply_mvar( FT_Face face );
FT_LOCAL( FT_Error )
- tt_var_load_item_variation_store( TT_Face face,
+ tt_var_load_item_variation_store( FT_Face face,
FT_ULong offset,
GX_ItemVarStore itemStore );
FT_LOCAL( FT_Error )
- tt_var_load_delta_set_index_mapping( TT_Face face,
+ tt_var_load_delta_set_index_mapping( FT_Face face,
FT_ULong offset,
GX_DeltaSetIdxMap map,
GX_ItemVarStore itemStore,
FT_ULong table_len );
FT_LOCAL( FT_ItemVarDelta )
- tt_var_get_item_delta( TT_Face face,
+ tt_var_get_item_delta( FT_Face face,
GX_ItemVarStore itemStore,
FT_UInt outerIndex,
FT_UInt innerIndex );
FT_LOCAL( void )
- tt_var_done_item_variation_store( TT_Face face,
+ tt_var_done_item_variation_store( FT_Face face,
GX_ItemVarStore itemStore );
FT_LOCAL( void )
- tt_var_done_delta_set_index_map( TT_Face face,
+ tt_var_done_delta_set_index_map( FT_Face face,
GX_DeltaSetIdxMap deltaSetIdxMap );
FT_LOCAL( FT_Error )
- tt_get_var_blend( TT_Face face,
+ tt_get_var_blend( FT_Face face,
FT_UInt *num_coords,
FT_Fixed* *coords,
FT_Fixed* *normalizedcoords,
FT_MM_Var* *mm_var );
FT_LOCAL( void )
- tt_done_blend( TT_Face face );
+ tt_done_blend( FT_Face face );
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
diff --git a/thirdparty/freetype/src/truetype/ttinterp.c b/thirdparty/freetype/src/truetype/ttinterp.c
index 4fcfaa3e43..34c3e6c92a 100644
--- a/thirdparty/freetype/src/truetype/ttinterp.c
+++ b/thirdparty/freetype/src/truetype/ttinterp.c
@@ -278,57 +278,6 @@
/**************************************************************************
*
* @Function:
- * Update_Max
- *
- * @Description:
- * Checks the size of a buffer and reallocates it if necessary.
- *
- * @Input:
- * memory ::
- * A handle to the parent memory object.
- *
- * multiplier ::
- * The size in bytes of each element in the buffer.
- *
- * new_max ::
- * The new capacity (size) of the buffer.
- *
- * @InOut:
- * size ::
- * The address of the buffer's current size expressed
- * in elements.
- *
- * buff ::
- * The address of the buffer base pointer.
- *
- * @Return:
- * FreeType error code. 0 means success.
- */
- FT_LOCAL_DEF( FT_Error )
- Update_Max( FT_Memory memory,
- FT_ULong* size,
- FT_ULong multiplier,
- void* _pbuff,
- FT_ULong new_max )
- {
- FT_Error error;
- void** pbuff = (void**)_pbuff;
-
-
- if ( *size < new_max )
- {
- if ( FT_QREALLOC( *pbuff, *size * multiplier, new_max * multiplier ) )
- return error;
- *size = new_max;
- }
-
- return FT_Err_Ok;
- }
-
-
- /**************************************************************************
- *
- * @Function:
* TT_Load_Context
*
* @Description:
@@ -359,9 +308,9 @@
TT_Size size )
{
FT_Int i;
- FT_ULong tmp;
TT_MaxProfile* maxp;
FT_Error error;
+ FT_Memory memory = exec->memory;
exec->face = face;
@@ -406,25 +355,15 @@
/* XXX: We reserve a little more elements on the stack to deal safely */
/* with broken fonts like arialbs, courbs, timesbs, etc. */
- tmp = (FT_ULong)exec->stackSize;
- error = Update_Max( exec->memory,
- &tmp,
- sizeof ( FT_F26Dot6 ),
- (void*)&exec->stack,
- maxp->maxStackElements + 32 );
- exec->stackSize = (FT_Long)tmp;
- if ( error )
+ if ( FT_QRENEW_ARRAY( exec->stack,
+ exec->stackSize,
+ maxp->maxStackElements + 32 ) )
return error;
+ exec->stackSize = maxp->maxStackElements + 32;
- tmp = (FT_ULong)exec->glyphSize;
- error = Update_Max( exec->memory,
- &tmp,
- sizeof ( FT_Byte ),
- (void*)&exec->glyphIns,
- maxp->maxSizeOfInstructions );
- exec->glyphSize = (FT_UInt)tmp;
- if ( error )
- return error;
+ /* free previous glyph code range */
+ FT_FREE( exec->glyphIns );
+ exec->glyphSize = 0;
exec->pts.n_points = 0;
exec->pts.n_contours = 0;
@@ -1530,14 +1469,16 @@
if ( exc->iniRange == tt_coderange_glyph &&
exc->cvt != exc->glyfCvt )
{
- exc->error = Update_Max( exc->memory,
- &exc->glyfCvtSize,
- sizeof ( FT_Long ),
- (void*)&exc->glyfCvt,
- exc->cvtSize );
- if ( exc->error )
+ FT_Memory memory = exc->memory;
+ FT_Error error;
+
+
+ FT_MEM_QRENEW_ARRAY( exc->glyfCvt, exc->glyfCvtSize, exc->cvtSize );
+ exc->error = error;
+ if ( error )
return;
+ exc->glyfCvtSize = exc->cvtSize;
FT_ARRAY_COPY( exc->glyfCvt, exc->cvt, exc->glyfCvtSize );
exc->cvt = exc->glyfCvt;
}
@@ -3117,18 +3058,18 @@
if ( exc->iniRange == tt_coderange_glyph &&
exc->storage != exc->glyfStorage )
{
- FT_ULong tmp = (FT_ULong)exc->glyfStoreSize;
+ FT_Memory memory = exc->memory;
+ FT_Error error;
- exc->error = Update_Max( exc->memory,
- &tmp,
- sizeof ( FT_Long ),
- (void*)&exc->glyfStorage,
- exc->storeSize );
- exc->glyfStoreSize = (FT_UShort)tmp;
- if ( exc->error )
+ FT_MEM_QRENEW_ARRAY( exc->glyfStorage,
+ exc->glyfStoreSize,
+ exc->storeSize );
+ exc->error = error;
+ if ( error )
return;
+ exc->glyfStoreSize = exc->storeSize;
FT_ARRAY_COPY( exc->glyfStorage, exc->storage, exc->glyfStoreSize );
exc->storage = exc->glyfStorage;
}
@@ -3606,7 +3547,8 @@
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
/* arguments to opcodes are skipped by `SKIP_Code' */
- FT_Byte opcode_pattern[9][12] = {
+ FT_Byte opcode_pattern[9][12] =
+ {
/* #0 inline delta function 1 */
{
0x4B, /* PPEM */
@@ -7380,14 +7322,6 @@
* GETINFO[]: GET INFOrmation
* Opcode range: 0x88
* Stack: uint32 --> uint32
- *
- * XXX: UNDOCUMENTED: Selector bits higher than 9 are currently (May
- * 2015) not documented in the OpenType specification.
- *
- * Selector bit 11 is incorrectly described as bit 8, while the
- * real meaning of bit 8 (vertical LCD subpixels) stays
- * undocumented. The same mistake can be found in Greg Hitchcock's
- * whitepaper.
*/
static void
Ins_GETINFO( TT_ExecContext exc,
@@ -7446,8 +7380,6 @@
* VARIATION GLYPH
* Selector Bit: 3
* Return Bit(s): 10
- *
- * XXX: UNDOCUMENTED!
*/
if ( (args[0] & 8 ) != 0 && exc->face->blend )
K |= 1 << 10;
@@ -7739,20 +7671,23 @@
/* documentation is in ttinterp.h */
FT_EXPORT_DEF( FT_Error )
- TT_RunIns( TT_ExecContext exc )
+ TT_RunIns( void* exec )
{
+ TT_ExecContext exc = (TT_ExecContext)exec;
+
FT_ULong ins_counter = 0; /* executed instructions counter */
FT_ULong num_twilight_points;
FT_UShort i;
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
- FT_Byte opcode_pattern[1][2] = {
- /* #8 TypeMan Talk Align */
- {
- 0x06, /* SPVTL */
- 0x7D, /* RDTG */
- },
- };
+ FT_Byte opcode_pattern[1][2] =
+ {
+ /* #8 TypeMan Talk Align */
+ {
+ 0x06, /* SPVTL */
+ 0x7D, /* RDTG */
+ },
+ };
FT_UShort opcode_patterns = 1;
FT_UShort opcode_pointer[1] = { 0 };
FT_UShort opcode_size[1] = { 1 };
@@ -7906,7 +7841,7 @@
/* a variable number of arguments */
/* it is the job of the application to `activate' GX handling, */
- /* this is, calling any of the GX API functions on the current */
+ /* that is, calling any of the GX API functions on the current */
/* font to select a variation instance */
if ( exc->face->blend )
exc->new_top = exc->args + exc->face->blend->num_axis;
@@ -8466,7 +8401,7 @@
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
case 0x91:
/* it is the job of the application to `activate' GX handling, */
- /* this is, calling any of the GX API functions on the current */
+ /* that is, calling any of the GX API functions on the current */
/* font to select a variation instance */
if ( exc->face->blend )
Ins_GETVARIATION( exc, args );
@@ -8604,7 +8539,7 @@
#else /* !TT_USE_BYTECODE_INTERPRETER */
/* ANSI C doesn't like empty source files */
- typedef int _tt_interp_dummy;
+ typedef int tt_interp_dummy_;
#endif /* !TT_USE_BYTECODE_INTERPRETER */
diff --git a/thirdparty/freetype/src/truetype/ttinterp.h b/thirdparty/freetype/src/truetype/ttinterp.h
index c54c053b29..48d493af80 100644
--- a/thirdparty/freetype/src/truetype/ttinterp.h
+++ b/thirdparty/freetype/src/truetype/ttinterp.h
@@ -460,14 +460,6 @@ FT_BEGIN_HEADER
FT_LOCAL( void )
TT_Clear_CodeRange( TT_ExecContext exec,
FT_Int range );
-
-
- FT_LOCAL( FT_Error )
- Update_Max( FT_Memory memory,
- FT_ULong* size,
- FT_ULong multiplier,
- void* _pbuff,
- FT_ULong new_max );
#endif /* TT_USE_BYTECODE_INTERPRETER */
@@ -536,7 +528,7 @@ FT_BEGIN_HEADER
* invoked by the TrueType debugger.
*/
FT_EXPORT( FT_Error )
- TT_RunIns( TT_ExecContext exec );
+ TT_RunIns( void* exec );
FT_END_HEADER
diff --git a/thirdparty/freetype/src/truetype/ttobjs.c b/thirdparty/freetype/src/truetype/ttobjs.c
index 4a8873fd8c..958fa54d42 100644
--- a/thirdparty/freetype/src/truetype/ttobjs.c
+++ b/thirdparty/freetype/src/truetype/ttobjs.c
@@ -312,7 +312,8 @@
#define TRICK_SFNT_IDS_NUM_FACES 31
static const tt_sfnt_id_rec sfnt_id[TRICK_SFNT_IDS_NUM_FACES]
- [TRICK_SFNT_IDS_PER_FACE] = {
+ [TRICK_SFNT_IDS_PER_FACE] =
+ {
#define TRICK_SFNT_ID_cvt 0
#define TRICK_SFNT_ID_fpgm 1
@@ -581,7 +582,7 @@
FT_Bool result = FALSE;
TT_Face face = (TT_Face)ttface;
- FT_UInt asize;
+ FT_ULong asize;
FT_ULong i;
FT_ULong glyph_index = 0;
FT_UInt count = 0;
@@ -589,7 +590,7 @@
for( i = 0; i < face->num_locations; i++ )
{
- tt_face_get_location( face, i, &asize );
+ tt_face_get_location( ttface, i, &asize );
if ( asize > 0 )
{
count += 1;
@@ -777,7 +778,6 @@
}
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
-
{
FT_UInt instance_index = (FT_UInt)face_index >> 16;
@@ -785,14 +785,11 @@
if ( FT_HAS_MULTIPLE_MASTERS( ttface ) &&
instance_index > 0 )
{
- error = TT_Set_Named_Instance( face, instance_index );
+ error = FT_Set_Named_Instance( ttface, instance_index );
if ( error )
goto Exit;
-
- tt_apply_mvar( face );
}
}
-
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
/* initialize standard glyph loading routines */
@@ -858,7 +855,7 @@
face->cvt_program_size = 0;
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
- tt_done_blend( face );
+ tt_done_blend( ttface );
face->blend = NULL;
#endif
}
@@ -1338,39 +1335,29 @@
/**************************************************************************
*
* @Function:
- * tt_size_reset
+ * tt_size_reset_height
*
* @Description:
- * Reset a TrueType size when resolutions and character dimensions
- * have been changed.
+ * Recompute a TrueType size's ascender, descender, and height
+ * when resolutions and character dimensions have been changed.
+ * Used for variation fonts as an iterator function.
*
* @Input:
- * size ::
- * A handle to the target size object.
- *
- * only_height ::
- * Only recompute ascender, descender, and height;
- * this flag is used for variation fonts where
- * `tt_size_reset' is used as an iterator function.
+ * ft_size ::
+ * A handle to the target TT_Size object. This function will be called
+ * through a `FT_Size_Reset_Func` pointer which takes `FT_Size`. This
+ * function must take `FT_Size` as a result. The passed `FT_Size` is
+ * expected to point to a `TT_Size`.
*/
FT_LOCAL_DEF( FT_Error )
- tt_size_reset( TT_Size size,
- FT_Bool only_height )
+ tt_size_reset_height( FT_Size ft_size )
{
- TT_Face face;
- FT_Size_Metrics* size_metrics;
-
-
- face = (TT_Face)size->root.face;
-
- /* nothing to do for CFF2 */
- if ( face->is_cff2 )
- return FT_Err_Ok;
+ TT_Size size = (TT_Size)ft_size;
+ TT_Face face = (TT_Face)size->root.face;
+ FT_Size_Metrics* size_metrics = &size->hinted_metrics;
size->ttmetrics.valid = FALSE;
- size_metrics = &size->hinted_metrics;
-
/* copy the result from base layer */
*size_metrics = size->root.metrics;
@@ -1397,12 +1384,34 @@
size->ttmetrics.valid = TRUE;
- if ( only_height )
- {
- /* we must not recompute the scaling values here since */
- /* `tt_size_reset' was already called (with only_height = 0) */
- return FT_Err_Ok;
- }
+ return FT_Err_Ok;
+ }
+
+
+ /**************************************************************************
+ *
+ * @Function:
+ * tt_size_reset
+ *
+ * @Description:
+ * Reset a TrueType size when resolutions and character dimensions
+ * have been changed.
+ *
+ * @Input:
+ * size ::
+ * A handle to the target size object.
+ */
+ FT_LOCAL_DEF( FT_Error )
+ tt_size_reset( TT_Size size )
+ {
+ FT_Error error;
+ TT_Face face = (TT_Face)size->root.face;
+ FT_Size_Metrics* size_metrics = &size->hinted_metrics;
+
+
+ error = tt_size_reset_height( (FT_Size)size );
+ if ( error )
+ return error;
if ( face->header.Flags & 8 )
{
diff --git a/thirdparty/freetype/src/truetype/ttobjs.h b/thirdparty/freetype/src/truetype/ttobjs.h
index bc6fbe7f19..d1834c046f 100644
--- a/thirdparty/freetype/src/truetype/ttobjs.h
+++ b/thirdparty/freetype/src/truetype/ttobjs.h
@@ -391,8 +391,10 @@ FT_BEGIN_HEADER
#endif /* TT_USE_BYTECODE_INTERPRETER */
FT_LOCAL( FT_Error )
- tt_size_reset( TT_Size size,
- FT_Bool only_height );
+ tt_size_reset_height( FT_Size size );
+
+ FT_LOCAL( FT_Error )
+ tt_size_reset( TT_Size size );
/**************************************************************************
diff --git a/thirdparty/freetype/src/truetype/ttpload.c b/thirdparty/freetype/src/truetype/ttpload.c
index e08bf309e3..54a64c7b46 100644
--- a/thirdparty/freetype/src/truetype/ttpload.c
+++ b/thirdparty/freetype/src/truetype/ttpload.c
@@ -180,10 +180,11 @@
FT_LOCAL_DEF( FT_ULong )
- tt_face_get_location( TT_Face face,
- FT_UInt gindex,
- FT_UInt *asize )
+ tt_face_get_location( FT_Face face, /* TT_Face */
+ FT_UInt gindex,
+ FT_ULong *asize )
{
+ TT_Face ttface = (TT_Face)face;
FT_ULong pos1, pos2;
FT_Byte* p;
FT_Byte* p_limit;
@@ -191,12 +192,12 @@
pos1 = pos2 = 0;
- if ( gindex < face->num_locations )
+ if ( gindex < ttface->num_locations )
{
- if ( face->header.Index_To_Loc_Format != 0 )
+ if ( ttface->header.Index_To_Loc_Format != 0 )
{
- p = face->glyph_locations + gindex * 4;
- p_limit = face->glyph_locations + face->num_locations * 4;
+ p = ttface->glyph_locations + gindex * 4;
+ p_limit = ttface->glyph_locations + ttface->num_locations * 4;
pos1 = FT_NEXT_ULONG( p );
pos2 = pos1;
@@ -206,8 +207,8 @@
}
else
{
- p = face->glyph_locations + gindex * 2;
- p_limit = face->glyph_locations + face->num_locations * 2;
+ p = ttface->glyph_locations + gindex * 2;
+ p_limit = ttface->glyph_locations + ttface->num_locations * 2;
pos1 = FT_NEXT_USHORT( p );
pos2 = pos1;
@@ -221,30 +222,30 @@
}
/* Check broken location data. */
- if ( pos1 > face->glyf_len )
+ if ( pos1 > ttface->glyf_len )
{
FT_TRACE1(( "tt_face_get_location:"
" too large offset (0x%08lx) found for glyph index %d,\n",
pos1, gindex ));
FT_TRACE1(( " "
" exceeding the end of `glyf' table (0x%08lx)\n",
- face->glyf_len ));
+ ttface->glyf_len ));
*asize = 0;
return 0;
}
- if ( pos2 > face->glyf_len )
+ if ( pos2 > ttface->glyf_len )
{
/* We try to sanitize the last `loca' entry. */
- if ( gindex == face->num_locations - 2 )
+ if ( gindex == ttface->num_locations - 2 )
{
FT_TRACE1(( "tt_face_get_location:"
" too large size (%ld bytes) found for glyph index %d,\n",
pos2 - pos1, gindex ));
FT_TRACE1(( " "
" truncating at the end of `glyf' table to %ld bytes\n",
- face->glyf_len - pos1 ));
- pos2 = face->glyf_len;
+ ttface->glyf_len - pos1 ));
+ pos2 = ttface->glyf_len;
}
else
{
@@ -253,7 +254,7 @@
pos2, gindex + 1 ));
FT_TRACE1(( " "
" exceeding the end of `glyf' table (0x%08lx)\n",
- face->glyf_len ));
+ ttface->glyf_len ));
*asize = 0;
return 0;
}
@@ -268,9 +269,9 @@
/* We get (intentionally) a wrong, non-zero result in case the */
/* `glyf' table is missing. */
if ( pos2 >= pos1 )
- *asize = (FT_UInt)( pos2 - pos1 );
+ *asize = (FT_ULong)( pos2 - pos1 );
else
- *asize = (FT_UInt)( face->glyf_len - pos1 );
+ *asize = (FT_ULong)( ttface->glyf_len - pos1 );
return pos1;
}
diff --git a/thirdparty/freetype/src/truetype/ttpload.h b/thirdparty/freetype/src/truetype/ttpload.h
index 939e02fe4f..ed229fa461 100644
--- a/thirdparty/freetype/src/truetype/ttpload.h
+++ b/thirdparty/freetype/src/truetype/ttpload.h
@@ -31,9 +31,9 @@ FT_BEGIN_HEADER
FT_Stream stream );
FT_LOCAL( FT_ULong )
- tt_face_get_location( TT_Face face,
- FT_UInt gindex,
- FT_UInt *asize );
+ tt_face_get_location( FT_Face face,
+ FT_UInt gindex,
+ FT_ULong *asize );
FT_LOCAL( void )
tt_face_done_loca( TT_Face face );
diff --git a/thirdparty/freetype/src/truetype/ttsubpix.c b/thirdparty/freetype/src/truetype/ttsubpix.c
index d811beef0d..aed4a1a274 100644
--- a/thirdparty/freetype/src/truetype/ttsubpix.c
+++ b/thirdparty/freetype/src/truetype/ttsubpix.c
@@ -1004,7 +1004,7 @@
/* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY) */
/* ANSI C doesn't like empty source files */
- typedef int _tt_subpix_dummy;
+ typedef int tt_subpix_dummy_;
#endif /* !(TT_USE_BYTECODE_INTERPRETER && */
/* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY) */
diff --git a/thirdparty/freetype/src/type1/t1afm.c b/thirdparty/freetype/src/type1/t1afm.c
index 787aa92c98..d9b9398b01 100644
--- a/thirdparty/freetype/src/type1/t1afm.c
+++ b/thirdparty/freetype/src/type1/t1afm.c
@@ -299,7 +299,7 @@
/* ascender and descender are optional and could both be zero */
/* check if values are meaningful before overriding defaults */
if ( fi->Ascender > fi->Descender )
- {
+ {
/* no `U' suffix here to 0x8000! */
t1_face->ascender = (FT_Short)( ( fi->Ascender + 0x8000 ) >> 16 );
t1_face->descender = (FT_Short)( ( fi->Descender + 0x8000 ) >> 16 );
@@ -405,7 +405,7 @@
#else /* T1_CONFIG_OPTION_NO_AFM */
/* ANSI C doesn't like empty source files */
- typedef int _t1_afm_dummy;
+ typedef int t1_afm_dummy_;
#endif /* T1_CONFIG_OPTION_NO_AFM */
diff --git a/thirdparty/freetype/src/type1/t1driver.c b/thirdparty/freetype/src/type1/t1driver.c
index ded3b264e8..a4cdf372a9 100644
--- a/thirdparty/freetype/src/type1/t1driver.c
+++ b/thirdparty/freetype/src/type1/t1driver.c
@@ -56,28 +56,32 @@
*
*/
- static FT_Error
- t1_get_glyph_name( T1_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ t1_get_glyph_name( FT_Face face, /* T1_Face */
FT_UInt glyph_index,
FT_Pointer buffer,
FT_UInt buffer_max )
{
- FT_STRCPYN( buffer, face->type1.glyph_names[glyph_index], buffer_max );
+ T1_Face t1face = (T1_Face)face;
+
+
+ FT_STRCPYN( buffer, t1face->type1.glyph_names[glyph_index], buffer_max );
return FT_Err_Ok;
}
- static FT_UInt
- t1_get_name_index( T1_Face face,
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_get_name_index( FT_Face face, /* T1_Face */
const FT_String* glyph_name )
{
- FT_Int i;
+ T1_Face t1face = (T1_Face)face;
+ FT_Int i;
- for ( i = 0; i < face->type1.num_glyphs; i++ )
+ for ( i = 0; i < t1face->type1.num_glyphs; i++ )
{
- FT_String* gname = face->type1.glyph_names[i];
+ FT_String* gname = t1face->type1.glyph_names[i];
if ( !ft_strcmp( glyph_name, gname ) )
@@ -90,8 +94,8 @@
static const FT_Service_GlyphDictRec t1_service_glyph_dict =
{
- (FT_GlyphDict_GetNameFunc) t1_get_glyph_name, /* get_name */
- (FT_GlyphDict_NameIndexFunc)t1_get_name_index /* name_index */
+ t1_get_glyph_name, /* FT_GlyphDict_GetNameFunc get_name */
+ t1_get_name_index /* FT_GlyphDict_NameIndexFunc name_index */
};
@@ -101,9 +105,12 @@
*/
static const char*
- t1_get_ps_name( T1_Face face )
+ t1_get_ps_name( FT_Face face ) /* T1_Face */
{
- return (const char*) face->type1.font_name;
+ T1_Face t1face = (T1_Face)face;
+
+
+ return (const char*) t1face->type1.font_name;
}
@@ -121,30 +128,28 @@
#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
static const FT_Service_MultiMastersRec t1_service_multi_masters =
{
- (FT_Get_MM_Func) T1_Get_Multi_Master, /* get_mm */
- (FT_Set_MM_Design_Func) T1_Set_MM_Design, /* set_mm_design */
- (FT_Set_MM_Blend_Func) T1_Set_MM_Blend, /* set_mm_blend */
- (FT_Get_MM_Blend_Func) T1_Get_MM_Blend, /* get_mm_blend */
- (FT_Get_MM_Var_Func) T1_Get_MM_Var, /* get_mm_var */
- (FT_Set_Var_Design_Func)T1_Set_Var_Design, /* set_var_design */
- (FT_Get_Var_Design_Func)T1_Get_Var_Design, /* get_var_design */
- (FT_Set_Instance_Func) T1_Reset_MM_Blend, /* set_instance */
- (FT_Set_MM_WeightVector_Func)
- T1_Set_MM_WeightVector, /* set_mm_weightvector */
- (FT_Get_MM_WeightVector_Func)
- T1_Get_MM_WeightVector, /* get_mm_weightvector */
- (FT_Var_Load_Delta_Set_Idx_Map_Func)
- NULL, /* load_delta_set_idx_map */
- (FT_Var_Load_Item_Var_Store_Func)
- NULL, /* load_item_variation_store */
- (FT_Var_Get_Item_Delta_Func)
- NULL, /* get_item_delta */
- (FT_Var_Done_Item_Var_Store_Func)
- NULL, /* done_item_variation_store */
- (FT_Var_Done_Delta_Set_Idx_Map_Func)
- NULL, /* done_delta_set_index_map */
- (FT_Get_Var_Blend_Func) NULL, /* get_var_blend */
- (FT_Done_Blend_Func) T1_Done_Blend /* done_blend */
+ T1_Get_Multi_Master, /* FT_Get_MM_Func get_mm */
+ T1_Set_MM_Design, /* FT_Set_MM_Design_Func set_mm_design */
+ T1_Set_MM_Blend, /* FT_Set_MM_Blend_Func set_mm_blend */
+ T1_Get_MM_Blend, /* FT_Get_MM_Blend_Func get_mm_blend */
+ T1_Get_MM_Var, /* FT_Get_MM_Var_Func get_mm_var */
+ T1_Set_Var_Design, /* FT_Set_Var_Design_Func set_var_design */
+ T1_Get_Var_Design, /* FT_Get_Var_Design_Func get_var_design */
+ T1_Reset_MM_Blend, /* FT_Set_Named_Instance_Func set_named_instance */
+ NULL, /* FT_Get_Default_Named_Instance_Func get_default_named_instance */
+ T1_Set_MM_WeightVector,
+ /* FT_Set_MM_WeightVector_Func set_mm_weightvector */
+ T1_Get_MM_WeightVector,
+ /* FT_Get_MM_WeightVector_Func get_mm_weightvector */
+
+ NULL, /* FT_Construct_PS_Name_Func construct_ps_name */
+ NULL, /* FT_Var_Load_Delta_Set_Idx_Map_Func load_delta_set_idx_map */
+ NULL, /* FT_Var_Load_Item_Var_Store_Func load_item_variation_store */
+ NULL, /* FT_Var_Get_Item_Delta_Func get_item_delta */
+ NULL, /* FT_Var_Done_Item_Var_Store_Func done_item_variation_store */
+ NULL, /* FT_Var_Done_Delta_Set_Idx_Map_Func done_delta_set_index_map */
+ NULL, /* FT_Get_Var_Blend_Func get_var_blend */
+ T1_Done_Blend /* FT_Done_Blend_Func done_blend */
};
#endif
@@ -632,11 +637,11 @@
static const FT_Service_PsInfoRec t1_service_ps_info =
{
- (PS_GetFontInfoFunc) t1_ps_get_font_info, /* ps_get_font_info */
- (PS_GetFontExtraFunc) t1_ps_get_font_extra, /* ps_get_font_extra */
- (PS_HasGlyphNamesFunc) t1_ps_has_glyph_names, /* ps_has_glyph_names */
- (PS_GetFontPrivateFunc)t1_ps_get_font_private, /* ps_get_font_private */
- (PS_GetFontValueFunc) t1_ps_get_font_value, /* ps_get_font_value */
+ t1_ps_get_font_info, /* PS_GetFontInfoFunc ps_get_font_info */
+ t1_ps_get_font_extra, /* PS_GetFontExtraFunc ps_get_font_extra */
+ t1_ps_has_glyph_names, /* PS_HasGlyphNamesFunc ps_has_glyph_names */
+ t1_ps_get_font_private, /* PS_GetFontPrivateFunc ps_get_font_private */
+ t1_ps_get_font_value, /* PS_GetFontValueFunc ps_get_font_value */
};
@@ -656,9 +661,9 @@
FT_DEFINE_SERVICE_PROPERTIESREC(
t1_service_properties,
- (FT_Properties_SetFunc)ps_property_set, /* set_property */
- (FT_Properties_GetFunc)ps_property_get ) /* get_property */
-
+ ps_property_set, /* FT_Properties_SetFunc set_property */
+ ps_property_get /* FT_Properties_GetFunc get_property */
+ )
/*
* SERVICE LIST
diff --git a/thirdparty/freetype/src/type1/t1load.c b/thirdparty/freetype/src/type1/t1load.c
index 5a1afd8d9f..b292a3cc0e 100644
--- a/thirdparty/freetype/src/type1/t1load.c
+++ b/thirdparty/freetype/src/type1/t1load.c
@@ -73,7 +73,8 @@
#ifdef FT_CONFIG_OPTION_INCREMENTAL
-#define IS_INCREMENTAL FT_BOOL( face->root.internal->incremental_interface )
+#define IS_INCREMENTAL \
+ FT_BOOL( FT_FACE( face )->internal->incremental_interface )
#else
#define IS_INCREMENTAL 0
#endif
@@ -174,10 +175,11 @@
FT_LOCAL_DEF( FT_Error )
- T1_Get_Multi_Master( T1_Face face,
+ T1_Get_Multi_Master( FT_Face face, /* T1_Face */
FT_Multi_Master* master )
{
- PS_Blend blend = face->blend;
+ T1_Face t1face = (T1_Face)face;
+ PS_Blend blend = t1face->blend;
FT_UInt n;
FT_Error error;
@@ -225,11 +227,12 @@
for ( j = 1; j < axismap->num_points; j++ )
{
if ( ncv <= axismap->blend_points[j] )
- return INT_TO_FIXED( axismap->design_points[j - 1] ) +
- ( axismap->design_points[j] - axismap->design_points[j - 1] ) *
- FT_DivFix( ncv - axismap->blend_points[j - 1],
- axismap->blend_points[j] -
- axismap->blend_points[j - 1] );
+ return INT_TO_FIXED( axismap->design_points[j - 1] +
+ FT_MulDiv( ncv - axismap->blend_points[j - 1],
+ axismap->design_points[j] -
+ axismap->design_points[j - 1],
+ axismap->blend_points[j] -
+ axismap->blend_points[j - 1] ) );
}
return INT_TO_FIXED( axismap->design_points[axismap->num_points - 1] );
@@ -284,16 +287,17 @@
* arguments needed by the GX var distortable fonts.
*/
FT_LOCAL_DEF( FT_Error )
- T1_Get_MM_Var( T1_Face face,
+ T1_Get_MM_Var( FT_Face face, /* T1_Face */
FT_MM_Var* *master )
{
- FT_Memory memory = face->root.memory;
- FT_MM_Var *mmvar = NULL;
+ T1_Face t1face = (T1_Face)face;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_MM_Var *mmvar = NULL;
FT_Multi_Master mmaster;
FT_Error error;
FT_UInt i;
FT_Fixed axiscoords[T1_MAX_MM_AXIS];
- PS_Blend blend = face->blend;
+ PS_Blend blend = t1face->blend;
FT_UShort* axis_flags;
FT_Offset mmvar_size;
@@ -319,9 +323,9 @@
sizeof ( FT_UShort ) );
axis_size = mmaster.num_axis * sizeof ( FT_Var_Axis );
- if ( FT_ALLOC( mmvar, mmvar_size +
- axis_flags_size +
- axis_size ) )
+ if ( FT_QALLOC( mmvar, mmvar_size +
+ axis_flags_size +
+ axis_size ) )
goto Exit;
mmvar->num_axis = mmaster.num_axis;
@@ -332,8 +336,7 @@
/* to make `FT_Get_Var_Axis_Flags' work: the function expects that the */
/* values directly follow the data of `FT_MM_Var' */
axis_flags = (FT_UShort*)( (char*)mmvar + mmvar_size );
- for ( i = 0; i < mmaster.num_axis; i++ )
- axis_flags[i] = 0;
+ FT_ARRAY_ZERO( axis_flags, mmaster.num_axis );
mmvar->axis = (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
mmvar->namedstyle = NULL;
@@ -438,32 +441,21 @@
FT_LOCAL_DEF( FT_Error )
- T1_Set_MM_Blend( T1_Face face,
+ T1_Set_MM_Blend( FT_Face face, /* T1_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
- FT_Error error;
-
-
- error = t1_set_mm_blend( face, num_coords, coords );
- if ( error )
- return error;
-
- if ( num_coords )
- face->root.face_flags |= FT_FACE_FLAG_VARIATION;
- else
- face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
-
- return FT_Err_Ok;
+ return t1_set_mm_blend( (T1_Face)face, num_coords, coords );
}
FT_LOCAL_DEF( FT_Error )
- T1_Get_MM_Blend( T1_Face face,
+ T1_Get_MM_Blend( FT_Face face, /* T1_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
- PS_Blend blend = face->blend;
+ T1_Face t1face = (T1_Face)face;
+ PS_Blend blend = t1face->blend;
FT_Fixed axiscoords[4];
FT_UInt i, nc;
@@ -494,11 +486,12 @@
FT_LOCAL_DEF( FT_Error )
- T1_Set_MM_WeightVector( T1_Face face,
+ T1_Set_MM_WeightVector( FT_Face face, /* T1_Face */
FT_UInt len,
FT_Fixed* weightvector )
{
- PS_Blend blend = face->blend;
+ T1_Face t1face = (T1_Face)face;
+ PS_Blend blend = t1face->blend;
FT_UInt i, n;
@@ -522,11 +515,6 @@
for ( ; i < blend->num_designs; i++ )
blend->weight_vector[i] = (FT_Fixed)0;
-
- if ( len )
- face->root.face_flags |= FT_FACE_FLAG_VARIATION;
- else
- face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
}
return FT_Err_Ok;
@@ -534,11 +522,12 @@
FT_LOCAL_DEF( FT_Error )
- T1_Get_MM_WeightVector( T1_Face face,
+ T1_Get_MM_WeightVector( FT_Face face, /* T1_Face */
FT_UInt* len,
FT_Fixed* weightvector )
{
- PS_Blend blend = face->blend;
+ T1_Face t1face = (T1_Face)face;
+ PS_Blend blend = t1face->blend;
FT_UInt i;
@@ -563,12 +552,13 @@
FT_LOCAL_DEF( FT_Error )
- T1_Set_MM_Design( T1_Face face,
+ T1_Set_MM_Design( FT_Face face, /* T1_Face */
FT_UInt num_coords,
FT_Long* coords )
{
+ T1_Face t1face = (T1_Face)face;
FT_Error error;
- PS_Blend blend = face->blend;
+ PS_Blend blend = t1face->blend;
FT_UInt n;
FT_Fixed final_blends[T1_MAX_MM_DESIGNS];
@@ -634,15 +624,10 @@
final_blends[n] = the_blend;
}
- error = t1_set_mm_blend( face, blend->num_axis, final_blends );
+ error = t1_set_mm_blend( t1face, blend->num_axis, final_blends );
if ( error )
return error;
- if ( num_coords )
- face->root.face_flags |= FT_FACE_FLAG_VARIATION;
- else
- face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
-
return FT_Err_Ok;
}
@@ -650,7 +635,7 @@
/* MM fonts don't have named instances, so only the design is reset */
FT_LOCAL_DEF( FT_Error )
- T1_Reset_MM_Blend( T1_Face face,
+ T1_Reset_MM_Blend( FT_Face face,
FT_UInt instance_index )
{
FT_UNUSED( instance_index );
@@ -665,7 +650,7 @@
* arguments needed by the GX var distortable fonts.
*/
FT_LOCAL_DEF( FT_Error )
- T1_Set_Var_Design( T1_Face face,
+ T1_Set_Var_Design( FT_Face face, /* T1_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
@@ -684,11 +669,12 @@
FT_LOCAL_DEF( FT_Error )
- T1_Get_Var_Design( T1_Face face,
+ T1_Get_Var_Design( FT_Face face, /* T1_Face */
FT_UInt num_coords,
FT_Fixed* coords )
{
- PS_Blend blend = face->blend;
+ T1_Face t1face = (T1_Face)face;
+ PS_Blend blend = t1face->blend;
FT_Fixed axiscoords[4];
FT_UInt i, nc;
@@ -720,10 +706,11 @@
FT_LOCAL_DEF( void )
- T1_Done_Blend( T1_Face face )
+ T1_Done_Blend( FT_Face face ) /* T1_Face */
{
- FT_Memory memory = face->root.memory;
- PS_Blend blend = face->blend;
+ T1_Face t1face = (T1_Face)face;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ PS_Blend blend = t1face->blend;
if ( blend )
@@ -768,20 +755,22 @@
dmap->num_points = 0;
}
- FT_FREE( face->blend );
+ FT_FREE( t1face->blend );
}
}
static void
- parse_blend_axis_types( T1_Face face,
- T1_Loader loader )
+ parse_blend_axis_types( FT_Face face, /* T1_Face */
+ void* loader_ )
{
+ T1_Face t1face = (T1_Face)face;
+ T1_Loader loader = (T1_Loader)loader_;
T1_TokenRec axis_tokens[T1_MAX_MM_AXIS];
FT_Int n, num_axis;
- FT_Error error = FT_Err_Ok;
+ FT_Error error = FT_Err_Ok;
PS_Blend blend;
- FT_Memory memory;
+ FT_Memory memory = FT_FACE_MEMORY( face );
/* take an array of objects */
@@ -801,14 +790,13 @@
}
/* allocate blend if necessary */
- error = t1_allocate_blend( face, 0, (FT_UInt)num_axis );
+ error = t1_allocate_blend( t1face, 0, (FT_UInt)num_axis );
if ( error )
goto Exit;
FT_TRACE4(( " [" ));
- blend = face->blend;
- memory = face->root.memory;
+ blend = t1face->blend;
/* each token is an immediate containing the name of the axis */
for ( n = 0; n < num_axis; n++ )
@@ -856,14 +844,16 @@
static void
- parse_blend_design_positions( T1_Face face,
- T1_Loader loader )
+ parse_blend_design_positions( FT_Face face, /* T1_Face */
+ void* loader_ )
{
+ T1_Face t1face = (T1_Face)face;
+ T1_Loader loader = (T1_Loader)loader_;
T1_TokenRec design_tokens[T1_MAX_MM_DESIGNS];
FT_Int num_designs;
FT_Int num_axis = 0; /* make compiler happy */
T1_Parser parser = &loader->parser;
- FT_Memory memory = face->root.memory;
+ FT_Memory memory = FT_FACE_MEMORY( face );
FT_Error error = FT_Err_Ok;
FT_Fixed* design_pos[T1_MAX_MM_DESIGNS];
@@ -921,7 +911,7 @@
}
num_axis = n_axis;
- error = t1_allocate_blend( face,
+ error = t1_allocate_blend( t1face,
(FT_UInt)num_designs,
(FT_UInt)num_axis );
if ( error )
@@ -962,7 +952,7 @@
loader->parser.root.limit = old_limit;
/* a valid BlendDesignPosition has been parsed */
- blend = face->blend;
+ blend = t1face->blend;
if ( blend->design_pos[0] )
FT_FREE( blend->design_pos[0] );
@@ -980,9 +970,11 @@
static void
- parse_blend_design_map( T1_Face face,
- T1_Loader loader )
+ parse_blend_design_map( FT_Face face, /* T1_Face */
+ void* loader_ )
{
+ T1_Face t1face = (T1_Face)face;
+ T1_Loader loader = (T1_Loader)loader_;
FT_Error error = FT_Err_Ok;
T1_Parser parser = &loader->parser;
PS_Blend blend;
@@ -990,7 +982,7 @@
FT_Int n, num_axis;
FT_Byte* old_cursor;
FT_Byte* old_limit;
- FT_Memory memory = face->root.memory;
+ FT_Memory memory = FT_FACE_MEMORY( face );
T1_ToTokenArray( parser, axis_tokens,
@@ -1011,10 +1003,10 @@
old_cursor = parser->root.cursor;
old_limit = parser->root.limit;
- error = t1_allocate_blend( face, 0, (FT_UInt)num_axis );
+ error = t1_allocate_blend( t1face, 0, (FT_UInt)num_axis );
if ( error )
goto Exit;
- blend = face->blend;
+ blend = t1face->blend;
FT_TRACE4(( " [" ));
@@ -1089,15 +1081,17 @@
static void
- parse_weight_vector( T1_Face face,
- T1_Loader loader )
+ parse_weight_vector( FT_Face face, /* T1_Face */
+ void* loader_ )
{
+ T1_Face t1face = (T1_Face)face;
+ T1_Loader loader = (T1_Loader)loader_;
T1_TokenRec design_tokens[T1_MAX_MM_DESIGNS];
FT_Int num_designs;
FT_Error error = FT_Err_Ok;
- FT_Memory memory = face->root.memory;
+ FT_Memory memory = FT_FACE_MEMORY( face );
T1_Parser parser = &loader->parser;
- PS_Blend blend = face->blend;
+ PS_Blend blend = t1face->blend;
T1_Token token;
FT_Int n;
FT_Byte* old_cursor;
@@ -1122,10 +1116,10 @@
if ( !blend || !blend->num_designs )
{
- error = t1_allocate_blend( face, (FT_UInt)num_designs, 0 );
+ error = t1_allocate_blend( t1face, (FT_UInt)num_designs, 0 );
if ( error )
goto Exit;
- blend = face->blend;
+ blend = t1face->blend;
}
else if ( blend->num_designs != (FT_UInt)num_designs )
{
@@ -1173,11 +1167,15 @@
/* e.g., /BuildCharArray [0 0 0 0 0 0 0 0] def */
/* we're only interested in the number of array elements */
static void
- parse_buildchar( T1_Face face,
- T1_Loader loader )
+ parse_buildchar( FT_Face face, /* T1_Face */
+ void* loader_ )
{
- face->len_buildchar = (FT_UInt)T1_ToFixedArray( &loader->parser,
- 0, NULL, 0 );
+ T1_Face t1face = (T1_Face)face;
+ T1_Loader loader = (T1_Loader)loader_;
+
+
+ t1face->len_buildchar = (FT_UInt)T1_ToFixedArray( &loader->parser,
+ 0, NULL, 0 );
#ifdef FT_DEBUG_LEVEL_TRACE
{
@@ -1185,7 +1183,7 @@
FT_TRACE4(( " [" ));
- for ( i = 0; i < face->len_buildchar; i++ )
+ for ( i = 0; i < t1face->len_buildchar; i++ )
FT_TRACE4(( " 0" ));
FT_TRACE4(( "]\n" ));
@@ -1335,9 +1333,10 @@
static void
- parse_private( T1_Face face,
- T1_Loader loader )
+ parse_private( FT_Face face,
+ void* loader_ )
{
+ T1_Loader loader = (T1_Loader)loader_;
FT_UNUSED( face );
loader->keywords_encountered |= T1_PRIVATE;
@@ -1401,13 +1400,14 @@
/* and `/CharStrings' dictionaries. */
static void
- t1_parse_font_matrix( T1_Face face,
- T1_Loader loader )
+ t1_parse_font_matrix( FT_Face face, /* T1_Face */
+ void* loader_ )
{
+ T1_Face t1face = (T1_Face)face;
+ T1_Loader loader = (T1_Loader)loader_;
T1_Parser parser = &loader->parser;
- FT_Matrix* matrix = &face->type1.font_matrix;
- FT_Vector* offset = &face->type1.font_offset;
- FT_Face root = (FT_Face)&face->root;
+ FT_Matrix* matrix = &t1face->type1.font_matrix;
+ FT_Vector* offset = &t1face->type1.font_offset;
FT_Fixed temp[6];
FT_Fixed temp_scale;
FT_Int result;
@@ -1443,7 +1443,7 @@
if ( temp_scale != 0x10000L )
{
/* set units per EM based on FontMatrix values */
- root->units_per_EM = (FT_UShort)FT_DivFix( 1000, temp_scale );
+ face->units_per_EM = (FT_UShort)FT_DivFix( 1000, temp_scale );
temp[0] = FT_DivFix( temp[0], temp_scale );
temp[1] = FT_DivFix( temp[1], temp_scale );
@@ -1471,14 +1471,16 @@
static void
- parse_encoding( T1_Face face,
- T1_Loader loader )
+ parse_encoding( FT_Face face, /* T1_Face */
+ void* loader_ )
{
+ T1_Face t1face = (T1_Face)face;
+ T1_Loader loader = (T1_Loader)loader_;
T1_Parser parser = &loader->parser;
FT_Byte* cur;
FT_Byte* limit = parser->root.limit;
- PSAux_Service psaux = (PSAux_Service)face->psaux;
+ PSAux_Service psaux = (PSAux_Service)t1face->psaux;
T1_Skip_Spaces( parser );
@@ -1494,7 +1496,7 @@
/* and we must load it now */
if ( ft_isdigit( *cur ) || *cur == '[' )
{
- T1_Encoding encode = &face->type1.encoding;
+ T1_Encoding encode = &t1face->type1.encoding;
FT_Int count, array_size, n;
PS_Table char_table = &loader->encoding_table;
FT_Memory memory = parser->root.memory;
@@ -1676,7 +1678,7 @@
FT_TRACE4(( "]\n" ));
#endif
- face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY;
+ t1face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY;
parser->root.cursor = cur;
}
@@ -1687,21 +1689,21 @@
if ( cur + 17 < limit &&
ft_strncmp( (const char*)cur, "StandardEncoding", 16 ) == 0 )
{
- face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD;
+ t1face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD;
FT_TRACE4(( " StandardEncoding\n" ));
}
else if ( cur + 15 < limit &&
ft_strncmp( (const char*)cur, "ExpertEncoding", 14 ) == 0 )
{
- face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT;
+ t1face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT;
FT_TRACE4(( " ExpertEncoding\n" ));
}
else if ( cur + 18 < limit &&
ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 )
{
- face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1;
+ t1face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1;
FT_TRACE4(( " ISOLatin1Encoding\n" ));
}
@@ -1715,9 +1717,11 @@
static void
- parse_subrs( T1_Face face,
- T1_Loader loader )
+ parse_subrs( FT_Face face, /* T1_Face */
+ void* loader_ )
{
+ T1_Face t1face = (T1_Face)face;
+ T1_Loader loader = (T1_Loader)loader_;
T1_Parser parser = &loader->parser;
PS_Table table = &loader->subrs;
FT_Memory memory = parser->root.memory;
@@ -1725,7 +1729,7 @@
FT_Int num_subrs;
FT_UInt count;
- PSAux_Service psaux = (PSAux_Service)face->psaux;
+ PSAux_Service psaux = (PSAux_Service)t1face->psaux;
T1_Skip_Spaces( parser );
@@ -1857,7 +1861,7 @@
/* */
/* thanks to Tom Kacvinsky for pointing this out */
/* */
- if ( face->type1.private_dict.lenIV >= 0 )
+ if ( t1face->type1.private_dict.lenIV >= 0 )
{
FT_Byte* temp = NULL;
@@ -1865,7 +1869,7 @@
/* some fonts define empty subr records -- this is not totally */
/* compliant to the specification (which says they should at */
/* least contain a `return'), but we support them anyway */
- if ( size < (FT_ULong)face->type1.private_dict.lenIV )
+ if ( size < (FT_ULong)t1face->type1.private_dict.lenIV )
{
error = FT_THROW( Invalid_File_Format );
goto Fail;
@@ -1876,9 +1880,11 @@
goto Fail;
FT_MEM_COPY( temp, base, size );
psaux->t1_decrypt( temp, size, 4330 );
- size -= (FT_ULong)face->type1.private_dict.lenIV;
- error = T1_Add_Table( table, (FT_Int)idx,
- temp + face->type1.private_dict.lenIV, size );
+ size -= (FT_ULong)t1face->type1.private_dict.lenIV;
+ error = T1_Add_Table( table,
+ (FT_Int)idx,
+ temp + t1face->type1.private_dict.lenIV,
+ size );
FT_FREE( temp );
}
else
@@ -1910,9 +1916,11 @@
static void
- parse_charstrings( T1_Face face,
- T1_Loader loader )
+ parse_charstrings( FT_Face face, /* T1_Face */
+ void* loader_ )
{
+ T1_Face t1face = (T1_Face)face;
+ T1_Loader loader = (T1_Loader)loader_;
T1_Parser parser = &loader->parser;
PS_Table code_table = &loader->charstrings;
PS_Table name_table = &loader->glyph_names;
@@ -1920,7 +1928,7 @@
FT_Memory memory = parser->root.memory;
FT_Error error;
- PSAux_Service psaux = (PSAux_Service)face->psaux;
+ PSAux_Service psaux = (PSAux_Service)t1face->psaux;
FT_Byte* cur = parser->root.cursor;
FT_Byte* limit = parser->root.limit;
@@ -2069,13 +2077,13 @@
notdef_found = 1;
}
- if ( face->type1.private_dict.lenIV >= 0 &&
+ if ( t1face->type1.private_dict.lenIV >= 0 &&
n < num_glyphs + TABLE_EXTEND )
{
FT_Byte* temp = NULL;
- if ( size <= (FT_ULong)face->type1.private_dict.lenIV )
+ if ( size <= (FT_ULong)t1face->type1.private_dict.lenIV )
{
error = FT_THROW( Invalid_File_Format );
goto Fail;
@@ -2086,9 +2094,11 @@
goto Fail;
FT_MEM_COPY( temp, base, size );
psaux->t1_decrypt( temp, size, 4330 );
- size -= (FT_ULong)face->type1.private_dict.lenIV;
- error = T1_Add_Table( code_table, n,
- temp + face->type1.private_dict.lenIV, size );
+ size -= (FT_ULong)t1face->type1.private_dict.lenIV;
+ error = T1_Add_Table( code_table,
+ n,
+ temp + t1face->type1.private_dict.lenIV,
+ size );
FT_FREE( temp );
}
else
@@ -2570,7 +2580,7 @@
{
FT_ERROR(( "T1_Open_Face:"
" number-of-designs != 2 ^^ number-of-axes\n" ));
- T1_Done_Blend( face );
+ T1_Done_Blend( FT_FACE( face ) );
}
if ( face->blend &&
@@ -2590,15 +2600,15 @@
/* font as a normal PS font */
if ( face->blend &&
( !face->blend->num_designs || !face->blend->num_axis ) )
- T1_Done_Blend( face );
+ T1_Done_Blend( FT_FACE( face ) );
/* the font may have no valid WeightVector */
if ( face->blend && !face->blend->weight_vector )
- T1_Done_Blend( face );
+ T1_Done_Blend( FT_FACE( face ) );
/* the font may have no valid BlendDesignPositions */
if ( face->blend && !face->blend->design_pos[0] )
- T1_Done_Blend( face );
+ T1_Done_Blend( FT_FACE( face ) );
/* the font may have no valid BlendDesignMap */
if ( face->blend )
@@ -2609,7 +2619,7 @@
for ( i = 0; i < face->blend->num_axis; i++ )
if ( !face->blend->design_map[i].num_points )
{
- T1_Done_Blend( face );
+ T1_Done_Blend( FT_FACE( face ) );
break;
}
}
diff --git a/thirdparty/freetype/src/type1/t1load.h b/thirdparty/freetype/src/type1/t1load.h
index f8511cccf6..d8c9d2d8ab 100644
--- a/thirdparty/freetype/src/type1/t1load.h
+++ b/thirdparty/freetype/src/type1/t1load.h
@@ -66,52 +66,52 @@ FT_BEGIN_HEADER
#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
FT_LOCAL( FT_Error )
- T1_Get_Multi_Master( T1_Face face,
+ T1_Get_Multi_Master( FT_Face face,
FT_Multi_Master* master );
FT_LOCAL( FT_Error )
- T1_Get_MM_Var( T1_Face face,
+ T1_Get_MM_Var( FT_Face face,
FT_MM_Var* *master );
FT_LOCAL( FT_Error )
- T1_Set_MM_Blend( T1_Face face,
+ T1_Set_MM_Blend( FT_Face face,
FT_UInt num_coords,
FT_Fixed* coords );
FT_LOCAL( FT_Error )
- T1_Get_MM_Blend( T1_Face face,
+ T1_Get_MM_Blend( FT_Face face,
FT_UInt num_coords,
FT_Fixed* coords );
FT_LOCAL( FT_Error )
- T1_Set_MM_Design( T1_Face face,
+ T1_Set_MM_Design( FT_Face face,
FT_UInt num_coords,
FT_Long* coords );
FT_LOCAL( FT_Error )
- T1_Reset_MM_Blend( T1_Face face,
+ T1_Reset_MM_Blend( FT_Face face,
FT_UInt instance_index );
FT_LOCAL( FT_Error )
- T1_Get_Var_Design( T1_Face face,
+ T1_Get_Var_Design( FT_Face face,
FT_UInt num_coords,
FT_Fixed* coords );
FT_LOCAL( FT_Error )
- T1_Set_Var_Design( T1_Face face,
+ T1_Set_Var_Design( FT_Face face,
FT_UInt num_coords,
FT_Fixed* coords );
FT_LOCAL( void )
- T1_Done_Blend( T1_Face face );
+ T1_Done_Blend( FT_Face face );
FT_LOCAL( FT_Error )
- T1_Set_MM_WeightVector( T1_Face face,
+ T1_Set_MM_WeightVector( FT_Face face,
FT_UInt len,
FT_Fixed* weightvector );
FT_LOCAL( FT_Error )
- T1_Get_MM_WeightVector( T1_Face face,
+ T1_Get_MM_WeightVector( FT_Face face,
FT_UInt* len,
FT_Fixed* weightvector );
diff --git a/thirdparty/freetype/src/type1/t1objs.c b/thirdparty/freetype/src/type1/t1objs.c
index 1bb2f15f3a..69e4fd5065 100644
--- a/thirdparty/freetype/src/type1/t1objs.c
+++ b/thirdparty/freetype/src/type1/t1objs.c
@@ -167,8 +167,7 @@
FT_Module module;
- module = FT_Get_Module( slot->face->driver->root.library,
- "pshinter" );
+ module = FT_Get_Module( slot->library, "pshinter" );
if ( module )
{
T1_Hints_Funcs funcs;
@@ -227,7 +226,7 @@
face->len_buildchar = 0;
}
- T1_Done_Blend( face );
+ T1_Done_Blend( t1face );
face->blend = NULL;
#endif
@@ -290,7 +289,8 @@
*
* @Input:
* stream ::
- * input stream where to load font data.
+ * Dummy argument for compatibility with the `FT_Face_InitFunc` API.
+ * Ignored. The stream should be passed through `face->root.stream`.
*
* face_index ::
* The index of the font face in the resource.
diff --git a/thirdparty/freetype/src/type42/t42drivr.c b/thirdparty/freetype/src/type42/t42drivr.c
index ce1528e5db..ee5fd44a9f 100644
--- a/thirdparty/freetype/src/type42/t42drivr.c
+++ b/thirdparty/freetype/src/type42/t42drivr.c
@@ -56,33 +56,41 @@
*
*/
- static FT_Error
- t42_get_glyph_name( T42_Face face,
+ FT_CALLBACK_DEF( FT_Error )
+ t42_get_glyph_name( FT_Face face, /* T42_Face */
FT_UInt glyph_index,
FT_Pointer buffer,
FT_UInt buffer_max )
{
- FT_STRCPYN( buffer, face->type1.glyph_names[glyph_index], buffer_max );
+ T42_Face t42face = (T42_Face)face;
+
+
+ FT_STRCPYN( buffer,
+ t42face->type1.glyph_names[glyph_index],
+ buffer_max );
return FT_Err_Ok;
}
- static FT_UInt
- t42_get_name_index( T42_Face face,
+ FT_CALLBACK_DEF( FT_UInt )
+ t42_get_name_index( FT_Face face, /* T42_Face */
const FT_String* glyph_name )
{
- FT_Int i;
+ T42_Face t42face = (T42_Face)face;
+ FT_Int i;
- for ( i = 0; i < face->type1.num_glyphs; i++ )
+ for ( i = 0; i < t42face->type1.num_glyphs; i++ )
{
- FT_String* gname = face->type1.glyph_names[i];
+ FT_String* gname = t42face->type1.glyph_names[i];
if ( glyph_name[0] == gname[0] && !ft_strcmp( glyph_name, gname ) )
- return (FT_UInt)ft_strtol( (const char *)face->type1.charstrings[i],
- NULL, 10 );
+ return (FT_UInt)ft_strtol(
+ (const char *)t42face->type1.charstrings[i],
+ NULL,
+ 10 );
}
return 0;
@@ -102,10 +110,13 @@
*
*/
- static const char*
- t42_get_ps_font_name( T42_Face face )
+ FT_CALLBACK_DEF( const char* )
+ t42_get_ps_font_name( FT_Face face ) /* T42_Face */
{
- return (const char*)face->type1.font_name;
+ T42_Face t42face = (T42_Face)face;
+
+
+ return (const char*)t42face->type1.font_name;
}
@@ -121,7 +132,7 @@
*
*/
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
t42_ps_get_font_info( FT_Face face,
PS_FontInfoRec* afont_info )
{
@@ -131,7 +142,7 @@
}
- static FT_Error
+ FT_CALLBACK_DEF( FT_Error )
t42_ps_get_font_extra( FT_Face face,
PS_FontExtraRec* afont_extra )
{
@@ -141,7 +152,7 @@
}
- static FT_Int
+ FT_CALLBACK_DEF( FT_Int )
t42_ps_has_glyph_names( FT_Face face )
{
FT_UNUSED( face );
diff --git a/thirdparty/freetype/src/type42/t42parse.c b/thirdparty/freetype/src/type42/t42parse.c
index 6d765c8c10..764bbd4c4d 100644
--- a/thirdparty/freetype/src/type42/t42parse.c
+++ b/thirdparty/freetype/src/type42/t42parse.c
@@ -34,19 +34,19 @@
static void
- t42_parse_font_matrix( T42_Face face,
- T42_Loader loader );
+ t42_parse_font_matrix( FT_Face face,
+ void* loader_ );
static void
- t42_parse_encoding( T42_Face face,
- T42_Loader loader );
+ t42_parse_encoding( FT_Face face,
+ void* loader_ );
static void
- t42_parse_charstrings( T42_Face face,
- T42_Loader loader );
+ t42_parse_charstrings( FT_Face face,
+ void* loader_ );
static void
- t42_parse_sfnts( T42_Face face,
- T42_Loader loader );
+ t42_parse_sfnts( FT_Face face,
+ void* loader_ );
/* as Type42 fonts have no Private dict, */
@@ -241,12 +241,14 @@
static void
- t42_parse_font_matrix( T42_Face face,
- T42_Loader loader )
+ t42_parse_font_matrix( FT_Face face, /* T42_Face */
+ void* loader_ )
{
- T42_Parser parser = &loader->parser;
- FT_Matrix* matrix = &face->type1.font_matrix;
- FT_Vector* offset = &face->type1.font_offset;
+ T42_Face t42face = (T42_Face)face;
+ T42_Loader loader = (T42_Loader)loader_;
+ T42_Parser parser = &loader->parser;
+ FT_Matrix* matrix = &t42face->type1.font_matrix;
+ FT_Vector* offset = &t42face->type1.font_offset;
FT_Fixed temp[6];
FT_Fixed temp_scale;
FT_Int result;
@@ -299,14 +301,16 @@
static void
- t42_parse_encoding( T42_Face face,
- T42_Loader loader )
+ t42_parse_encoding( FT_Face face,
+ void* loader_ )
{
- T42_Parser parser = &loader->parser;
+ T42_Face t42face = (T42_Face)face;
+ T42_Loader loader = (T42_Loader)loader_;
+ T42_Parser parser = &loader->parser;
FT_Byte* cur;
- FT_Byte* limit = parser->root.limit;
+ FT_Byte* limit = parser->root.limit;
- PSAux_Service psaux = (PSAux_Service)face->psaux;
+ PSAux_Service psaux = (PSAux_Service)t42face->psaux;
T1_Skip_Spaces( parser );
@@ -322,7 +326,7 @@
/* and we must load it now */
if ( ft_isdigit( *cur ) || *cur == '[' )
{
- T1_Encoding encode = &face->type1.encoding;
+ T1_Encoding encode = &t42face->type1.encoding;
FT_Int count, n;
PS_Table char_table = &loader->encoding_table;
FT_Memory memory = parser->root.memory;
@@ -493,8 +497,8 @@
T1_Skip_Spaces( parser );
}
- face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY;
- parser->root.cursor = cur;
+ t42face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY;
+ parser->root.cursor = cur;
}
/* Otherwise, we should have either `StandardEncoding', */
@@ -503,15 +507,15 @@
{
if ( cur + 17 < limit &&
ft_strncmp( (const char*)cur, "StandardEncoding", 16 ) == 0 )
- face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD;
+ t42face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD;
else if ( cur + 15 < limit &&
ft_strncmp( (const char*)cur, "ExpertEncoding", 14 ) == 0 )
- face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT;
+ t42face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT;
else if ( cur + 18 < limit &&
ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 )
- face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1;
+ t42face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1;
else
parser->root.error = FT_ERR( Ignore );
@@ -529,9 +533,11 @@
static void
- t42_parse_sfnts( T42_Face face,
- T42_Loader loader )
+ t42_parse_sfnts( FT_Face face,
+ void* loader_ )
{
+ T42_Face t42face = (T42_Face)face;
+ T42_Loader loader = (T42_Loader)loader_;
T42_Parser parser = &loader->parser;
FT_Memory memory = parser->root.memory;
FT_Byte* cur;
@@ -548,8 +554,8 @@
T42_Load_Status status;
/** There should only be one sfnts array, but free any previous. */
- FT_FREE( face->ttf_data );
- face->ttf_size = 0;
+ FT_FREE( t42face->ttf_data );
+ t42face->ttf_size = 0;
/* The format is */
/* */
@@ -580,7 +586,7 @@
old_string_size = 0;
ttf_count = 0;
ttf_reserved = 12;
- if ( FT_QALLOC( face->ttf_data, ttf_reserved ) )
+ if ( FT_QALLOC( t42face->ttf_data, ttf_reserved ) )
goto Fail;
FT_TRACE2(( "\n" ));
@@ -596,7 +602,7 @@
if ( *cur == ']' )
{
parser->root.cursor++;
- face->ttf_size = ttf_count;
+ t42face->ttf_size = ttf_count;
goto Exit;
}
@@ -707,7 +713,7 @@
/* load offset table, 12 bytes */
if ( ttf_count < 12 )
{
- face->ttf_data[ttf_count++] = string_buf[n];
+ t42face->ttf_data[ttf_count++] = string_buf[n];
continue;
}
else
@@ -715,7 +721,7 @@
FT_Long ttf_reserved_prev = ttf_reserved;
- num_tables = 16 * face->ttf_data[4] + face->ttf_data[5];
+ num_tables = 16 * t42face->ttf_data[4] + t42face->ttf_data[5];
status = BEFORE_TABLE_DIR;
ttf_reserved = 12 + 16 * num_tables;
@@ -729,7 +735,7 @@
goto Fail;
}
- if ( FT_QREALLOC( face->ttf_data, ttf_reserved_prev,
+ if ( FT_QREALLOC( t42face->ttf_data, ttf_reserved_prev,
ttf_reserved ) )
goto Fail;
}
@@ -739,7 +745,7 @@
/* the offset table is read; read the table directory */
if ( ttf_count < ttf_reserved )
{
- face->ttf_data[ttf_count++] = string_buf[n];
+ t42face->ttf_data[ttf_count++] = string_buf[n];
continue;
}
else
@@ -755,7 +761,7 @@
for ( i = 0; i < num_tables; i++ )
{
- FT_Byte* p = face->ttf_data + 12 + 16 * i + 12;
+ FT_Byte* p = t42face->ttf_data + 12 + 16 * i + 12;
len = FT_PEEK_ULONG( p );
@@ -781,7 +787,7 @@
FT_TRACE2(( " allocating %ld bytes\n", ttf_reserved ));
FT_TRACE2(( "\n" ));
- if ( FT_QREALLOC( face->ttf_data, ttf_reserved_prev,
+ if ( FT_QREALLOC( t42face->ttf_data, ttf_reserved_prev,
ttf_reserved ) )
goto Fail;
}
@@ -795,7 +801,7 @@
error = FT_THROW( Invalid_File_Format );
goto Fail;
}
- face->ttf_data[ttf_count++] = string_buf[n];
+ t42face->ttf_data[ttf_count++] = string_buf[n];
}
}
@@ -811,8 +817,8 @@
Exit:
if ( parser->root.error )
{
- FT_FREE( face->ttf_data );
- face->ttf_size = 0;
+ FT_FREE( t42face->ttf_data );
+ t42face->ttf_size = 0;
}
if ( allocated )
FT_FREE( string_buf );
@@ -820,9 +826,11 @@
static void
- t42_parse_charstrings( T42_Face face,
- T42_Loader loader )
+ t42_parse_charstrings( FT_Face face, /* T42_Face */
+ void* loader_ )
{
+ T42_Face t42face = (T42_Face)face;
+ T42_Loader loader = (T42_Loader)loader_;
T42_Parser parser = &loader->parser;
PS_Table code_table = &loader->charstrings;
PS_Table name_table = &loader->glyph_names;
@@ -830,7 +838,7 @@
FT_Memory memory = parser->root.memory;
FT_Error error;
- PSAux_Service psaux = (PSAux_Service)face->psaux;
+ PSAux_Service psaux = (PSAux_Service)t42face->psaux;
FT_Byte* cur;
FT_Byte* limit = parser->root.limit;
diff --git a/thirdparty/freetype/src/winfonts/winfnt.c b/thirdparty/freetype/src/winfonts/winfnt.c
index fa73ae4a93..1160e4ef36 100644
--- a/thirdparty/freetype/src/winfonts/winfnt.c
+++ b/thirdparty/freetype/src/winfonts/winfnt.c
@@ -624,31 +624,34 @@
static FT_Error
- fnt_cmap_init( FNT_CMap cmap,
+ fnt_cmap_init( FT_CMap cmap, /* FNT_CMap */
FT_Pointer pointer )
{
- FNT_Face face = (FNT_Face)FT_CMAP_FACE( cmap );
- FNT_Font font = face->font;
+ FNT_CMap fntcmap = (FNT_CMap)cmap;
+ FNT_Face face = (FNT_Face)FT_CMAP_FACE( cmap );
+ FNT_Font font = face->font;
FT_UNUSED( pointer );
- cmap->first = (FT_UInt32) font->header.first_char;
- cmap->count = (FT_UInt32)( font->header.last_char - cmap->first + 1 );
+ fntcmap->first = (FT_UInt32)font->header.first_char;
+ fntcmap->count = (FT_UInt32)( font->header.last_char -
+ fntcmap->first + 1 );
return 0;
}
static FT_UInt
- fnt_cmap_char_index( FNT_CMap cmap,
+ fnt_cmap_char_index( FT_CMap cmap, /* FNT_CMap */
FT_UInt32 char_code )
{
- FT_UInt gindex = 0;
+ FNT_CMap fntcmap = (FNT_CMap)cmap;
+ FT_UInt gindex = 0;
- char_code -= cmap->first;
- if ( char_code < cmap->count )
+ char_code -= fntcmap->first;
+ if ( char_code < fntcmap->count )
/* we artificially increase the glyph index; */
/* FNT_Load_Glyph reverts to the right one */
gindex = (FT_UInt)( char_code + 1 );
@@ -656,26 +659,27 @@
}
- static FT_UInt32
- fnt_cmap_char_next( FNT_CMap cmap,
+ static FT_UInt
+ fnt_cmap_char_next( FT_CMap cmap, /* FNT_CMap */
FT_UInt32 *pchar_code )
{
- FT_UInt gindex = 0;
- FT_UInt32 result = 0;
+ FNT_CMap fntcmap = (FNT_CMap)cmap;
+ FT_UInt gindex = 0;
+ FT_UInt32 result = 0;
FT_UInt32 char_code = *pchar_code + 1;
- if ( char_code <= cmap->first )
+ if ( char_code <= fntcmap->first )
{
- result = cmap->first;
+ result = fntcmap->first;
gindex = 1;
}
else
{
- char_code -= cmap->first;
- if ( char_code < cmap->count )
+ char_code -= fntcmap->first;
+ if ( char_code < fntcmap->count )
{
- result = cmap->first + char_code;
+ result = fntcmap->first + char_code;
gindex = (FT_UInt)( char_code + 1 );
}
}
diff --git a/thirdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh b/thirdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh
index b125052344..457039bfc6 100644
--- a/thirdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh
+++ b/thirdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh
@@ -397,7 +397,6 @@ struct IndexSubtableRecord
TRACE_SERIALIZE (this);
auto *subtable = c->serializer->start_embed<IndexSubtable> ();
- if (unlikely (!subtable)) return_trace (false);
if (unlikely (!c->serializer->extend_min (subtable))) return_trace (false);
auto *old_subtable = get_subtable (base);
@@ -545,7 +544,8 @@ struct IndexSubtableArray
const IndexSubtableRecord*>> *lookup /* OUT */) const
{
bool start_glyph_is_set = false;
- for (hb_codepoint_t new_gid = 0; new_gid < c->plan->num_output_glyphs (); new_gid++)
+ unsigned num_glyphs = c->plan->num_output_glyphs ();
+ for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++)
{
hb_codepoint_t old_gid;
if (unlikely (!c->plan->old_gid_for_new_gid (new_gid, &old_gid))) continue;
@@ -576,9 +576,6 @@ struct IndexSubtableArray
{
TRACE_SUBSET (this);
- auto *dst = c->serializer->start_embed<IndexSubtableArray> ();
- if (unlikely (!dst)) return_trace (false);
-
hb_vector_t<hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*>> lookup;
build_lookup (c, bitmap_size_context, &lookup);
if (unlikely (!c->serializer->propagate_error (lookup)))
@@ -993,12 +990,10 @@ CBLC::subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
- auto *cblc_prime = c->serializer->start_embed<CBLC> ();
-
// Use a vector as a secondary buffer as the tables need to be built in parallel.
hb_vector_t<char> cbdt_prime;
- if (unlikely (!cblc_prime)) return_trace (false);
+ auto *cblc_prime = c->serializer->start_embed<CBLC> ();
if (unlikely (!c->serializer->extend_min (cblc_prime))) return_trace (false);
cblc_prime->version = version;
diff --git a/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh b/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh
index 2a47984294..6591bb4380 100644
--- a/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh
+++ b/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh
@@ -409,7 +409,6 @@ struct ColorLine
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
- if (unlikely (!out)) return_trace (false);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
if (!c->serializer->check_assign (out->extend, extend, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
@@ -1434,6 +1433,7 @@ struct PaintComposite
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
+ c->check_ops (this->min_size) && // PainComposite can get exponential
src.sanitize (c, this) &&
backdrop.sanitize (c, this));
}
@@ -2167,7 +2167,7 @@ struct COLR
if (version == 0 && (!base_it || !layer_it))
return_trace (false);
- COLR *colr_prime = c->serializer->start_embed<COLR> ();
+ auto *colr_prime = c->serializer->start_embed<COLR> ();
if (unlikely (!c->serializer->extend_min (colr_prime))) return_trace (false);
if (version == 0)
diff --git a/thirdparty/harfbuzz/src/OT/Color/sbix/sbix.hh b/thirdparty/harfbuzz/src/OT/Color/sbix/sbix.hh
index 46ad3fd58e..ce8693cfb1 100644
--- a/thirdparty/harfbuzz/src/OT/Color/sbix/sbix.hh
+++ b/thirdparty/harfbuzz/src/OT/Color/sbix/sbix.hh
@@ -48,7 +48,6 @@ struct SBIXGlyph
{
TRACE_SERIALIZE (this);
SBIXGlyph* new_glyph = c->start_embed<SBIXGlyph> ();
- if (unlikely (!new_glyph)) return_trace (nullptr);
if (unlikely (!c->extend_min (new_glyph))) return_trace (nullptr);
new_glyph->xOffset = xOffset;
@@ -143,7 +142,6 @@ struct SBIXStrike
unsigned int num_output_glyphs = c->plan->num_output_glyphs ();
auto* out = c->serializer->start_embed<SBIXStrike> ();
- if (unlikely (!out)) return_trace (false);
auto snap = c->serializer->snapshot ();
if (unlikely (!c->serializer->extend (out, num_output_glyphs + 1))) return_trace (false);
out->ppem = ppem;
@@ -388,7 +386,6 @@ struct sbix
TRACE_SERIALIZE (this);
auto *out = c->serializer->start_embed<Array32OfOffset32To<SBIXStrike>> ();
- if (unlikely (!out)) return_trace (false);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
hb_vector_t<Offset32To<SBIXStrike>*> new_strikes;
@@ -423,8 +420,6 @@ struct sbix
{
TRACE_SUBSET (this);
- sbix *sbix_prime = c->serializer->start_embed<sbix> ();
- if (unlikely (!sbix_prime)) return_trace (false);
if (unlikely (!c->serializer->embed (this->version))) return_trace (false);
if (unlikely (!c->serializer->embed (this->flags))) return_trace (false);
diff --git a/thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh b/thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh
index 9ca88f788a..25056c9bc3 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh
@@ -57,6 +57,9 @@ struct Coverage
public:
DEFINE_SIZE_UNION (2, format);
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
diff --git a/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh
index 5d68e3d15e..3f598d40ef 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh
@@ -79,7 +79,7 @@ struct CoverageFormat1_3
{
if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len) / 2)
{
- for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
+ for (auto g : *glyphs)
if (get_coverage (g) != NOT_COVERED)
return true;
return false;
diff --git a/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh
index fa501d659d..9c87542356 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh
@@ -122,7 +122,7 @@ struct CoverageFormat2_4
{
if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2)
{
- for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
+ for (auto g : *glyphs)
if (get_coverage (g) != NOT_COVERED)
return true;
return false;
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh b/thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh
index c1ff796199..d995ba0d4c 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh
@@ -49,8 +49,6 @@ struct AttachPoint : Array16Of<HBUINT16>
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out)) return_trace (false);
-
return_trace (out->serialize (c->serializer, + iter ()));
}
};
@@ -202,7 +200,6 @@ struct CaretValueFormat3
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out)) return_trace (false);
if (!c->serializer->embed (caretValueFormat)) return_trace (false);
if (!c->serializer->embed (coordinate)) return_trace (false);
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh
index e7e3c5c6d1..8684f60ca5 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh
@@ -25,7 +25,9 @@ struct AnchorFormat3
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
+ if (unlikely (!c->check_struct (this))) return_trace (false);
+
+ return_trace (xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
}
void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
@@ -35,9 +37,9 @@ struct AnchorFormat3
*x = font->em_fscale_x (xCoordinate);
*y = font->em_fscale_y (yCoordinate);
- if (font->x_ppem || font->num_coords)
+ if ((font->x_ppem || font->num_coords) && xDeviceTable.sanitize (&c->sanitizer, this))
*x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache);
- if (font->y_ppem || font->num_coords)
+ if ((font->y_ppem || font->num_coords) && yDeviceTable.sanitize (&c->sanitizer, this))
*y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
}
@@ -45,7 +47,6 @@ struct AnchorFormat3
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out)) return_trace (false);
if (unlikely (!c->serializer->embed (format))) return_trace (false);
if (unlikely (!c->serializer->embed (xCoordinate))) return_trace (false);
if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false);
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh
index c442efa1ea..bd9b189739 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh
@@ -21,18 +21,25 @@ struct AnchorMatrix
if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
unsigned int count = rows * cols;
if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
+
+ if (c->lazy_some_gpos)
+ return_trace (true);
+
for (unsigned int i = 0; i < count; i++)
if (!matrixZ[i].sanitize (c, this)) return_trace (false);
return_trace (true);
}
- const Anchor& get_anchor (unsigned int row, unsigned int col,
- unsigned int cols, bool *found) const
+ const Anchor& get_anchor (hb_ot_apply_context_t *c,
+ unsigned int row, unsigned int col,
+ unsigned int cols, bool *found) const
{
*found = false;
if (unlikely (row >= rows || col >= cols)) return Null (Anchor);
- *found = !matrixZ[row * cols + col].is_null ();
- return this+matrixZ[row * cols + col];
+ auto &offset = matrixZ[row * cols + col];
+ if (unlikely (!offset.sanitize (&c->sanitizer, this))) return Null (Anchor);
+ *found = !offset.is_null ();
+ return this+offset;
}
template <typename Iterator,
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh
index b8773ba0aa..a459124dfe 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh
@@ -278,7 +278,6 @@ struct CursivePosFormat1
const hb_map_t &glyph_map = *c->plan->glyph_map;
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out)) return_trace (false);
auto it =
+ hb_zip (this+coverage, entryExitRecord)
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh
index ff43ffb8c5..34e2ab8f6e 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh
@@ -28,7 +28,7 @@ struct MarkArray : Array16Of<MarkRecord> /* Array of MarkRecords--in Cove
const Anchor& mark_anchor = this + record.markAnchor;
bool found;
- const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
+ const Anchor& glyph_anchor = anchors.get_anchor (c, glyph_index, mark_class, class_count, &found);
/* If this subtable doesn't have an anchor for this base and this class,
* return false such that the subsequent subtables have a chance at it. */
if (unlikely (!found)) return_trace (false);
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh
index 31329dfcb5..05e6c0fa54 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh
@@ -54,8 +54,9 @@ struct PairPosFormat2_4
return_trace (c->check_range ((const void *) values,
count,
stride) &&
- valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
- valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
+ (c->lazy_some_gpos ||
+ (valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
+ valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride))));
}
bool intersects (const hb_set_t *glyphs) const
@@ -298,11 +299,13 @@ struct PairPosFormat2_4
out->valueFormat2 = out->valueFormat2.drop_device_table_flags ();
}
+ unsigned total_len = len1 + len2;
+ hb_vector_t<unsigned> class2_idxs (+ hb_range ((unsigned) class2Count) | hb_filter (klass2_map));
for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
{
- for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
+ for (unsigned class2_idx : class2_idxs)
{
- unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
+ unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * total_len;
valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], &c->plan->layout_variation_idx_delta_map);
valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], &c->plan->layout_variation_idx_delta_map);
}
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh
index 9faff49909..db301bb816 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh
@@ -52,8 +52,9 @@ struct PairSet
unsigned int count = len;
const PairValueRecord *record = &firstPairValueRecord;
- return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
- closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride));
+ return_trace (c->lazy_some_gpos ||
+ (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
+ closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)));
}
bool intersects (const hb_set_t *glyphs,
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh
index 623e4e66b2..dff1f7316d 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh
@@ -90,6 +90,7 @@ struct SinglePosFormat1
bool
position_single (hb_font_t *font,
+ hb_blob_t *table_blob,
hb_direction_t direction,
hb_codepoint_t gid,
hb_glyph_position_t &pos) const
@@ -100,7 +101,7 @@ struct SinglePosFormat1
/* This is ugly... */
hb_buffer_t buffer;
buffer.props.direction = direction;
- OT::hb_ot_apply_context_t c (1, font, &buffer);
+ OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob);
valueFormat.apply_value (&c, this, values, pos);
return true;
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh
index e8f2d7c2c6..168ad3bb8c 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh
@@ -94,6 +94,7 @@ struct SinglePosFormat2
bool
position_single (hb_font_t *font,
+ hb_blob_t *table_blob,
hb_direction_t direction,
hb_codepoint_t gid,
hb_glyph_position_t &pos) const
@@ -105,7 +106,7 @@ struct SinglePosFormat2
/* This is ugly... */
hb_buffer_t buffer;
buffer.props.direction = direction;
- OT::hb_ot_apply_context_t c (1, font, &buffer);
+ OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob);
valueFormat.apply_value (&c, this,
&values[index * valueFormat.get_len ()],
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh
index 1aa451abcc..461a13d4b7 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh
@@ -118,21 +118,25 @@ struct ValueFormat : HBUINT16
auto *cache = c->var_store_cache;
/* pixel -> fractional pixel */
- if (format & xPlaDevice) {
- if (use_x_device) glyph_pos.x_offset += (base + get_device (values, &ret)).get_x_delta (font, store, cache);
+ if (format & xPlaDevice)
+ {
+ if (use_x_device) glyph_pos.x_offset += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache);
values++;
}
- if (format & yPlaDevice) {
- if (use_y_device) glyph_pos.y_offset += (base + get_device (values, &ret)).get_y_delta (font, store, cache);
+ if (format & yPlaDevice)
+ {
+ if (use_y_device) glyph_pos.y_offset += get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache);
values++;
}
- if (format & xAdvDevice) {
- if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store, cache);
+ if (format & xAdvDevice)
+ {
+ if (horizontal && use_x_device) glyph_pos.x_advance += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache);
values++;
}
- if (format & yAdvDevice) {
+ if (format & yAdvDevice)
+ {
/* y_advance values grow downward but font-space grows upward, hence negation */
- if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store, cache);
+ if (!horizontal && use_y_device) glyph_pos.y_advance -= get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache);
values++;
}
return ret;
@@ -174,6 +178,9 @@ struct ValueFormat : HBUINT16
if (format & xAdvance) x_adv = copy_value (c, new_format, xAdvance, *values++);
if (format & yAdvance) y_adv = copy_value (c, new_format, yAdvance, *values++);
+ if (!has_device ())
+ return;
+
if (format & xPlaDevice)
{
add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map);
@@ -233,14 +240,12 @@ struct ValueFormat : HBUINT16
if (format & ValueFormat::xAdvDevice)
{
-
(base + get_device (&(values[i]))).collect_variation_indices (c);
i++;
}
if (format & ValueFormat::yAdvDevice)
{
-
(base + get_device (&(values[i]))).collect_variation_indices (c);
i++;
}
@@ -277,11 +282,23 @@ struct ValueFormat : HBUINT16
{
return *static_cast<Offset16To<Device> *> (value);
}
- static inline const Offset16To<Device>& get_device (const Value* value, bool *worked=nullptr)
+ static inline const Offset16To<Device>& get_device (const Value* value)
{
- if (worked) *worked |= bool (*value);
return *static_cast<const Offset16To<Device> *> (value);
}
+ static inline const Device& get_device (const Value* value,
+ bool *worked,
+ const void *base,
+ hb_sanitize_context_t &c)
+ {
+ if (worked) *worked |= bool (*value);
+ auto &offset = *static_cast<const Offset16To<Device> *> (value);
+
+ if (unlikely (!offset.sanitize (&c, base)))
+ return Null(Device);
+
+ return base + offset;
+ }
void add_delta_to_value (HBINT16 *value,
const void *base,
@@ -340,25 +357,26 @@ struct ValueFormat : HBUINT16
bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
{
TRACE_SANITIZE (this);
- return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
+
+ if (unlikely (!c->check_range (values, get_size ()))) return_trace (false);
+
+ if (c->lazy_some_gpos)
+ return_trace (true);
+
+ return_trace (!has_device () || sanitize_value_devices (c, base, values));
}
bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
{
TRACE_SANITIZE (this);
- unsigned int len = get_len ();
+ unsigned size = get_size ();
- if (!c->check_range (values, count, get_size ())) return_trace (false);
+ if (!c->check_range (values, count, size)) return_trace (false);
- if (!has_device ()) return_trace (true);
+ if (c->lazy_some_gpos)
+ return_trace (true);
- for (unsigned int i = 0; i < count; i++) {
- if (!sanitize_value_devices (c, base, values))
- return_trace (false);
- values += len;
- }
-
- return_trace (true);
+ return_trace (sanitize_values_stride_unsafe (c, base, values, count, size));
}
/* Just sanitize referenced Device tables. Doesn't check the values themselves. */
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/Common.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/Common.hh
index 968bba0481..b849494d88 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/Common.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/Common.hh
@@ -8,8 +8,6 @@ namespace OT {
namespace Layout {
namespace GSUB_impl {
-typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t;
-
template<typename Iterator>
static void SingleSubst_serialize (hb_serialize_context_t *c,
Iterator it);
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/Ligature.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/Ligature.hh
index 8674a52fb5..db3fc55f77 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/Ligature.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/Ligature.hh
@@ -13,12 +13,13 @@ struct Ligature
public:
typename Types::HBGlyphID
ligGlyph; /* GlyphID of ligature to substitute */
- HeadlessArrayOf<typename Types::HBGlyphID>
+ HeadlessArray16Of<typename Types::HBGlyphID>
component; /* Array of component GlyphIDs--start
* with the second component--ordered
* in writing direction */
public:
DEFINE_SIZE_ARRAY (Types::size + 2, component);
+ DEFINE_SIZE_MAX (65536 * Types::HBGlyphID::static_size);
bool sanitize (hb_sanitize_context_t *c) const
{
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
index 2c2e1aa44f..916fa281b3 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
@@ -191,7 +191,6 @@ struct ReverseChainSingleSubstFormat1
TRACE_SERIALIZE (this);
auto *out = c->serializer->start_embed (this);
- if (unlikely (!c->serializer->check_success (out))) return_trace (false);
if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false);
diff --git a/thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh b/thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh
index ae3292f329..a26cf8c6a6 100644
--- a/thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh
+++ b/thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh
@@ -53,7 +53,7 @@ struct Sequence
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
- "replaced glyph at %u (multiple subtitution)",
+ "replaced glyph at %u (multiple substitution)",
c->buffer->idx - 1u);
}
diff --git a/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh
index d81fadf7c8..60858a5a58 100644
--- a/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh
+++ b/thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh
@@ -90,24 +90,36 @@ struct CompositeGlyphRecord
static void transform (const float (&matrix)[4],
hb_array_t<contour_point_t> points)
{
- auto arrayZ = points.arrayZ;
- unsigned count = points.length;
-
if (matrix[0] != 1.f || matrix[1] != 0.f ||
matrix[2] != 0.f || matrix[3] != 1.f)
- for (unsigned i = 0; i < count; i++)
- arrayZ[i].transform (matrix);
+ for (auto &point : points)
+ point.transform (matrix);
}
static void translate (const contour_point_t &trans,
hb_array_t<contour_point_t> points)
{
- auto arrayZ = points.arrayZ;
- unsigned count = points.length;
-
- if (trans.x != 0.f || trans.y != 0.f)
- for (unsigned i = 0; i < count; i++)
- arrayZ[i].translate (trans);
+ if (HB_OPTIMIZE_SIZE_VAL)
+ {
+ if (trans.x != 0.f || trans.y != 0.f)
+ for (auto &point : points)
+ point.translate (trans);
+ }
+ else
+ {
+ if (trans.x != 0.f && trans.y != 0.f)
+ for (auto &point : points)
+ point.translate (trans);
+ else
+ {
+ if (trans.x != 0.f)
+ for (auto &point : points)
+ point.x += trans.x;
+ else if (trans.y != 0.f)
+ for (auto &point : points)
+ point.y += trans.y;
+ }
+ }
}
void transform_points (hb_array_t<contour_point_t> points,
@@ -131,9 +143,8 @@ struct CompositeGlyphRecord
float matrix[4];
contour_point_t trans;
get_transformation (matrix, trans);
- points.alloc (points.length + 4); // For phantom points
- if (unlikely (!points.resize (points.length + 1))) return false;
- points.arrayZ[points.length - 1] = trans;
+ if (unlikely (!points.alloc (points.length + 4))) return false; // For phantom points
+ points.push (trans);
return true;
}
@@ -382,7 +393,7 @@ struct CompositeGlyph
{
/* last 4 points in points_with_deltas are phantom points and should not be included */
if (i >= points_with_deltas.length - 4) {
- free (o);
+ hb_free (o);
return false;
}
diff --git a/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh b/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh
index 2bd5fe8206..2611c1e21d 100644
--- a/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh
+++ b/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh
@@ -114,8 +114,8 @@ struct Glyph
if (type != EMPTY)
{
- plan->bounds_width_map.set (new_gid, xMax - xMin);
- plan->bounds_height_map.set (new_gid, yMax - yMin);
+ plan->bounds_width_vec[new_gid] = xMax - xMin;
+ plan->bounds_height_vec[new_gid] = yMax - yMin;
}
unsigned len = all_points.length;
@@ -124,10 +124,12 @@ struct Glyph
float topSideY = all_points[len - 2].y;
float bottomSideY = all_points[len - 1].y;
+ uint32_t hash = hb_hash (new_gid);
+
signed hori_aw = roundf (rightSideX - leftSideX);
if (hori_aw < 0) hori_aw = 0;
int lsb = roundf (xMin - leftSideX);
- plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
+ plan->hmtx_map.set_with_hash (new_gid, hash, hb_pair ((unsigned) hori_aw, lsb));
//flag value should be computed using non-empty glyphs
if (type != EMPTY && lsb != xMin)
plan->head_maxp_info.allXMinIsLsb = false;
@@ -135,7 +137,7 @@ struct Glyph
signed vert_aw = roundf (topSideY - bottomSideY);
if (vert_aw < 0) vert_aw = 0;
int tsb = roundf (topSideY - yMax);
- plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
+ plan->vmtx_map.set_with_hash (new_gid, hash, hb_pair ((unsigned) vert_aw, tsb));
}
bool compile_header_bytes (const hb_subset_plan_t *plan,
@@ -369,9 +371,11 @@ struct Glyph
}
#ifndef HB_NO_VAR
- glyf_accelerator.gvar->apply_deltas_to_points (gid,
- coords,
- points.as_array ().sub_array (old_length));
+ if (coords)
+ glyf_accelerator.gvar->apply_deltas_to_points (gid,
+ coords,
+ points.as_array ().sub_array (old_length),
+ phantom_only && type == SIMPLE);
#endif
// mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
@@ -379,7 +383,7 @@ struct Glyph
if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
{
if (unlikely (!points_with_deltas->resize (points.length))) return false;
- points_with_deltas->copy_vector (points);
+ *points_with_deltas = points;
}
switch (type) {
@@ -417,14 +421,17 @@ struct Glyph
for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
- float matrix[4];
- contour_point_t default_trans;
- item.get_transformation (matrix, default_trans);
+ if (comp_points) // Empty in case of phantom_only
+ {
+ float matrix[4];
+ contour_point_t default_trans;
+ item.get_transformation (matrix, default_trans);
- /* Apply component transformation & translation (with deltas applied) */
- item.transform_points (comp_points, matrix, points[comp_index]);
+ /* Apply component transformation & translation (with deltas applied) */
+ item.transform_points (comp_points, matrix, points[comp_index]);
+ }
- if (item.is_anchored ())
+ if (item.is_anchored () && !phantom_only)
{
unsigned int p1, p2;
item.get_anchor_points (p1, p2);
@@ -466,7 +473,10 @@ struct Glyph
assert (record_points.length == item_num_points);
auto component_coords = coords;
- if (item.is_reset_unspecified_axes ())
+ /* Copying coords is expensive; so we have put an arbitrary
+ * limit on the max number of coords for now. */
+ if (item.is_reset_unspecified_axes () ||
+ coords.length > HB_GLYF_VAR_COMPOSITE_MAX_AXES)
component_coords = hb_array<int> ();
coord_setter_t coord_setter (component_coords);
diff --git a/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh
index 555bcee346..1d42cc2925 100644
--- a/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh
+++ b/thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh
@@ -154,10 +154,9 @@ struct SimpleGlyph
{
int v = 0;
- unsigned count = points_.length;
- for (unsigned i = 0; i < count; i++)
+ for (auto &point : points_)
{
- unsigned flag = points_.arrayZ[i].flag;
+ unsigned flag = point.flag;
if (flag & short_flag)
{
if (unlikely (p + 1 > end)) return false;
@@ -175,7 +174,7 @@ struct SimpleGlyph
p += HBINT16::static_size;
}
}
- points_.arrayZ[i].*m = v;
+ point.*m = v;
}
return true;
}
@@ -192,9 +191,10 @@ struct SimpleGlyph
unsigned old_length = points.length;
points.alloc (points.length + num_points + 4, true); // Allocate for phantom points, to avoid a possible copy
- if (!points.resize (points.length + num_points, false)) return false;
+ if (unlikely (!points.resize (points.length + num_points, false))) return false;
auto points_ = points.as_array ().sub_array (old_length);
- hb_memset (points_.arrayZ, 0, sizeof (contour_point_t) * num_points);
+ if (!phantom_only)
+ hb_memset (points_.arrayZ, 0, sizeof (contour_point_t) * num_points);
if (phantom_only) return true;
for (int i = 0; i < num_contours; i++)
diff --git a/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh
index 26dc374eab..8099d3c126 100644
--- a/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh
+++ b/thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh
@@ -22,7 +22,7 @@ struct SubsetGlyph
bool serialize (hb_serialize_context_t *c,
bool use_short_loca,
- const hb_subset_plan_t *plan)
+ const hb_subset_plan_t *plan) const
{
TRACE_SERIALIZE (this);
@@ -40,7 +40,7 @@ struct SubsetGlyph
pad = 0;
while (pad_length > 0)
{
- c->embed (pad);
+ (void) c->embed (pad);
pad_length--;
}
diff --git a/thirdparty/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh b/thirdparty/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh
index 6dc6fd9ded..50cbece3ca 100644
--- a/thirdparty/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh
+++ b/thirdparty/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh
@@ -214,7 +214,7 @@ struct VarCompositeGlyphRecord
points.alloc (points.length + num_points + 4); // For phantom points
if (unlikely (!points.resize (points.length + num_points, false))) return false;
contour_point_t *rec_points = points.arrayZ + (points.length - num_points);
- memset (rec_points, 0, num_points * sizeof (rec_points[0]));
+ hb_memset (rec_points, 0, num_points * sizeof (rec_points[0]));
unsigned fl = flags;
diff --git a/thirdparty/harfbuzz/src/OT/glyf/coord-setter.hh b/thirdparty/harfbuzz/src/OT/glyf/coord-setter.hh
index df64ed5af7..cf05929362 100644
--- a/thirdparty/harfbuzz/src/OT/glyf/coord-setter.hh
+++ b/thirdparty/harfbuzz/src/OT/glyf/coord-setter.hh
@@ -16,6 +16,8 @@ struct coord_setter_t
int& operator [] (unsigned idx)
{
+ if (unlikely (idx >= HB_GLYF_VAR_COMPOSITE_MAX_AXES))
+ return Crap(int);
if (coords.length < idx + 1)
coords.resize (idx + 1);
return coords[idx];
diff --git a/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh b/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh
index 30106b2b98..d0a5a132f0 100644
--- a/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh
+++ b/thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh
@@ -12,24 +12,44 @@ namespace OT {
namespace glyf_impl {
-template<typename IteratorIn, typename IteratorOut,
- hb_requires (hb_is_source_of (IteratorIn, unsigned int)),
- hb_requires (hb_is_sink_of (IteratorOut, unsigned))>
+template<typename IteratorIn, typename TypeOut,
+ hb_requires (hb_is_source_of (IteratorIn, unsigned int))>
static void
-_write_loca (IteratorIn&& it, bool short_offsets, IteratorOut&& dest)
+_write_loca (IteratorIn&& it,
+ const hb_sorted_vector_t<hb_codepoint_pair_t> new_to_old_gid_list,
+ bool short_offsets,
+ TypeOut *dest,
+ unsigned num_offsets)
{
unsigned right_shift = short_offsets ? 1 : 0;
- unsigned int offset = 0;
- dest << 0;
- + it
- | hb_map ([=, &offset] (unsigned int padded_size)
- {
- offset += padded_size;
- DEBUG_MSG (SUBSET, nullptr, "loca entry offset %u", offset);
- return offset >> right_shift;
- })
- | hb_sink (dest)
- ;
+ unsigned offset = 0;
+ TypeOut value;
+ value = 0;
+ *dest++ = value;
+ hb_codepoint_t last = 0;
+ for (auto _ : new_to_old_gid_list)
+ {
+ hb_codepoint_t gid = _.first;
+ for (; last < gid; last++)
+ {
+ DEBUG_MSG (SUBSET, nullptr, "loca entry empty offset %u", offset);
+ *dest++ = value;
+ }
+
+ unsigned padded_size = *it++;
+ offset += padded_size;
+ DEBUG_MSG (SUBSET, nullptr, "loca entry gid %u offset %u padded-size %u", gid, offset, padded_size);
+ value = offset >> right_shift;
+ *dest++ = value;
+
+ last++; // Skip over gid
+ }
+ unsigned num_glyphs = num_offsets - 1;
+ for (; last < num_glyphs; last++)
+ {
+ DEBUG_MSG (SUBSET, nullptr, "loca entry empty offset %u", offset);
+ *dest++ = value;
+ }
}
static bool
@@ -67,11 +87,14 @@ _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
template<typename Iterator,
hb_requires (hb_is_source_of (Iterator, unsigned int))>
static bool
-_add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_short_loca)
+_add_loca_and_head (hb_subset_context_t *c,
+ Iterator padded_offsets,
+ bool use_short_loca)
{
- unsigned num_offsets = padded_offsets.len () + 1;
+ unsigned num_offsets = c->plan->num_output_glyphs () + 1;
unsigned entry_size = use_short_loca ? 2 : 4;
- char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets);
+
+ char *loca_prime_data = (char *) hb_malloc (entry_size * num_offsets);
if (unlikely (!loca_prime_data)) return false;
@@ -79,9 +102,9 @@ _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_s
entry_size, num_offsets, entry_size * num_offsets);
if (use_short_loca)
- _write_loca (padded_offsets, true, hb_array ((HBUINT16 *) loca_prime_data, num_offsets));
+ _write_loca (padded_offsets, c->plan->new_to_old_gid_list, true, (HBUINT16 *) loca_prime_data, num_offsets);
else
- _write_loca (padded_offsets, false, hb_array ((HBUINT32 *) loca_prime_data, num_offsets));
+ _write_loca (padded_offsets, c->plan->new_to_old_gid_list, false, (HBUINT32 *) loca_prime_data, num_offsets);
hb_blob_t *loca_blob = hb_blob_create (loca_prime_data,
entry_size * num_offsets,
@@ -89,8 +112,8 @@ _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_s
loca_prime_data,
hb_free);
- bool result = plan->add_table (HB_OT_TAG_loca, loca_blob)
- && _add_head_and_set_loca_version (plan, use_short_loca);
+ bool result = c->plan->add_table (HB_OT_TAG_loca, loca_blob)
+ && _add_head_and_set_loca_version (c->plan, use_short_loca);
hb_blob_destroy (loca_blob);
return result;
diff --git a/thirdparty/harfbuzz/src/OT/glyf/glyf.hh b/thirdparty/harfbuzz/src/OT/glyf/glyf.hh
index dd08dda6ee..6300cf4be0 100644
--- a/thirdparty/harfbuzz/src/OT/glyf/glyf.hh
+++ b/thirdparty/harfbuzz/src/OT/glyf/glyf.hh
@@ -85,75 +85,72 @@ struct glyf
return_trace (false);
}
- glyf *glyf_prime = c->serializer->start_embed <glyf> ();
- if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);
-
hb_font_t *font = nullptr;
if (c->plan->normalized_coords)
{
font = _create_font_for_instancing (c->plan);
- if (unlikely (!font)) return false;
+ if (unlikely (!font))
+ return_trace (false);
}
hb_vector_t<unsigned> padded_offsets;
- unsigned num_glyphs = c->plan->num_output_glyphs ();
- if (unlikely (!padded_offsets.resize (num_glyphs)))
- {
- hb_font_destroy (font);
- return false;
- }
+ if (unlikely (!padded_offsets.alloc (c->plan->new_to_old_gid_list.length, true)))
+ return_trace (false);
hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
if (!_populate_subset_glyphs (c->plan, font, glyphs))
{
hb_font_destroy (font);
- return false;
+ return_trace (false);
}
if (font)
hb_font_destroy (font);
unsigned max_offset = 0;
- for (unsigned i = 0; i < num_glyphs; i++)
+ for (auto &g : glyphs)
{
- padded_offsets[i] = glyphs[i].padded_size ();
- max_offset += padded_offsets[i];
+ unsigned size = g.padded_size ();
+ padded_offsets.push (size);
+ max_offset += size;
}
bool use_short_loca = false;
if (likely (!c->plan->force_long_loca))
use_short_loca = max_offset < 0x1FFFF;
- if (!use_short_loca) {
- for (unsigned i = 0; i < num_glyphs; i++)
- padded_offsets[i] = glyphs[i].length ();
+ if (!use_short_loca)
+ {
+ padded_offsets.resize (0);
+ for (auto &g : glyphs)
+ padded_offsets.push (g.length ());
}
- bool result = glyf_prime->serialize (c->serializer, glyphs.writer (), use_short_loca, c->plan);
+ auto *glyf_prime = c->serializer->start_embed <glyf> ();
+ bool result = glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan);
if (c->plan->normalized_coords && !c->plan->pinned_at_default)
_free_compiled_subset_glyphs (glyphs);
- if (!result) return false;
-
- if (unlikely (c->serializer->in_error ())) return_trace (false);
+ if (unlikely (!c->serializer->check_success (glyf_impl::_add_loca_and_head (c,
+ padded_offsets.iter (),
+ use_short_loca))))
+ return_trace (false);
- return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan,
- padded_offsets.iter (),
- use_short_loca)));
+ return result;
}
bool
_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_font_t *font,
- hb_vector_t<glyf_impl::SubsetGlyph> &glyphs /* OUT */) const;
+ hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const;
hb_font_t *
_create_font_for_instancing (const hb_subset_plan_t *plan) const;
void _free_compiled_subset_glyphs (hb_vector_t<glyf_impl::SubsetGlyph> &glyphs) const
{
- for (unsigned i = 0; i < glyphs.length; i++)
- glyphs[i].free_compiled_bytes ();
+ for (auto &g : glyphs)
+ g.free_compiled_bytes ();
}
protected:
@@ -222,13 +219,14 @@ struct glyf_accelerator_t
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only)))
return false;
+ unsigned count = all_points.length;
+ assert (count >= glyf_impl::PHANTOM_COUNT);
+ count -= glyf_impl::PHANTOM_COUNT;
+
if (consumer.is_consuming_contour_points ())
{
- unsigned count = all_points.length;
- assert (count >= glyf_impl::PHANTOM_COUNT);
- count -= glyf_impl::PHANTOM_COUNT;
- for (unsigned point_index = 0; point_index < count; point_index++)
- consumer.consume_point (all_points[point_index]);
+ for (auto &point : all_points.as_array ().sub_array (0, count))
+ consumer.consume_point (point);
consumer.points_end ();
}
@@ -236,7 +234,7 @@ struct glyf_accelerator_t
contour_point_t *phantoms = consumer.get_phantoms_sink ();
if (phantoms)
for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i)
- phantoms[i] = all_points[all_points.length - glyf_impl::PHANTOM_COUNT + i];
+ phantoms[i] = all_points.arrayZ[count + i];
return true;
}
@@ -299,6 +297,7 @@ struct glyf_accelerator_t
if (extents) bounds = contour_bounds_t ();
}
+ HB_ALWAYS_INLINE
void consume_point (const contour_point_t &point) { bounds.add (point); }
void points_end () { bounds.get_extents (font, extents, scaled); }
@@ -431,16 +430,17 @@ glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
{
OT::glyf_accelerator_t glyf (plan->source);
- unsigned num_glyphs = plan->num_output_glyphs ();
- if (!glyphs.resize (num_glyphs)) return false;
+ if (!glyphs.alloc (plan->new_to_old_gid_list.length, true)) return false;
- for (auto p : plan->glyph_map->iter ())
+ for (const auto &pair : plan->new_to_old_gid_list)
{
- unsigned new_gid = p.second;
- glyf_impl::SubsetGlyph& subset_glyph = glyphs.arrayZ[new_gid];
- subset_glyph.old_gid = p.first;
+ hb_codepoint_t new_gid = pair.first;
+ hb_codepoint_t old_gid = pair.second;
+ glyf_impl::SubsetGlyph *p = glyphs.push ();
+ glyf_impl::SubsetGlyph& subset_glyph = *p;
+ subset_glyph.old_gid = old_gid;
- if (unlikely (new_gid == 0 &&
+ if (unlikely (old_gid == 0 && new_gid == 0 &&
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
!plan->normalized_coords)
subset_glyph.source_glyph = glyf_impl::Glyph ();
@@ -487,7 +487,7 @@ glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const
{
hb_variation_t var;
var.tag = _.first;
- var.value = _.second;
+ var.value = _.second.middle;
vars.push (var);
}
diff --git a/thirdparty/harfbuzz/src/OT/glyf/path-builder.hh b/thirdparty/harfbuzz/src/OT/glyf/path-builder.hh
index f7f732d336..f550524503 100644
--- a/thirdparty/harfbuzz/src/OT/glyf/path-builder.hh
+++ b/thirdparty/harfbuzz/src/OT/glyf/path-builder.hh
@@ -21,11 +21,11 @@ struct path_builder_t
operator bool () const { return has_data; }
bool has_data = false;
- float x = 0.;
- float y = 0.;
+ float x;
+ float y;
- optional_point_t lerp (optional_point_t p, float t)
- { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); }
+ optional_point_t mid (optional_point_t p)
+ { return optional_point_t ((x + p.x) * 0.5f, (y + p.y) * 0.5f); }
} first_oncurve, first_offcurve, first_offcurve2, last_offcurve, last_offcurve2;
path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_) :
@@ -37,6 +37,7 @@ struct path_builder_t
* https://stackoverflow.com/a/20772557
*
* Cubic support added. */
+ HB_ALWAYS_INLINE
void consume_point (const contour_point_t &point)
{
bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
@@ -46,7 +47,7 @@ struct path_builder_t
bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
#endif
optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y));
- if (!first_oncurve)
+ if (unlikely (!first_oncurve))
{
if (is_on_curve)
{
@@ -62,7 +63,7 @@ struct path_builder_t
}
else if (first_offcurve)
{
- optional_point_t mid = first_offcurve.lerp (p, .5f);
+ optional_point_t mid = first_offcurve.mid (p);
first_oncurve = mid;
last_offcurve = p;
draw_session->move_to (mid.x, mid.y);
@@ -98,7 +99,7 @@ struct path_builder_t
}
else
{
- optional_point_t mid = last_offcurve.lerp (p, .5f);
+ optional_point_t mid = last_offcurve.mid (p);
if (is_cubic)
{
@@ -123,13 +124,13 @@ struct path_builder_t
}
}
- if (point.is_end_point)
+ if (unlikely (point.is_end_point))
{
if (first_offcurve && last_offcurve)
{
- optional_point_t mid = last_offcurve.lerp (first_offcurve2 ?
- first_offcurve2 :
- first_offcurve, .5f);
+ optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
+ first_offcurve2 :
+ first_offcurve);
if (last_offcurve2)
draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
last_offcurve.x, last_offcurve.y,
diff --git a/thirdparty/harfbuzz/src/OT/name/name.hh b/thirdparty/harfbuzz/src/OT/name/name.hh
index c1839f3b68..c8de101345 100644
--- a/thirdparty/harfbuzz/src/OT/name/name.hh
+++ b/thirdparty/harfbuzz/src/OT/name/name.hh
@@ -359,7 +359,7 @@ struct name
record.nameID = ids.name_id;
record.length = 0; // handled in NameRecord copy()
record.offset = 0;
- memcpy (name_records, &record, NameRecord::static_size);
+ hb_memcpy (name_records, &record, NameRecord::static_size);
name_records++;
}
#endif
@@ -384,10 +384,7 @@ struct name
bool subset (hb_subset_context_t *c) const
{
- TRACE_SUBSET (this);
-
- name *name_prime = c->serializer->start_embed<name> ();
- if (unlikely (!name_prime)) return_trace (false);
+ auto *name_prime = c->serializer->start_embed<name> ();
#ifdef HB_EXPERIMENTAL_API
const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides =
@@ -436,7 +433,7 @@ struct name
if (!name_table_overrides->is_empty ())
{
if (unlikely (!insert_name_records.alloc (name_table_overrides->get_population (), true)))
- return_trace (false);
+ return false;
for (const auto& record_ids : name_table_overrides->keys ())
{
if (name_table_overrides->get (record_ids).length == 0)
@@ -448,13 +445,13 @@ struct name
}
#endif
- return (name_prime->serialize (c->serializer, it,
- std::addressof (this + stringOffset)
+ return name_prime->serialize (c->serializer, it,
+ std::addressof (this + stringOffset)
#ifdef HB_EXPERIMENTAL_API
- , insert_name_records
- , name_table_overrides
+ , insert_name_records
+ , name_table_overrides
#endif
- ));
+ );
}
bool sanitize_records (hb_sanitize_context_t *c) const
diff --git a/thirdparty/harfbuzz/src/graph/classdef-graph.hh b/thirdparty/harfbuzz/src/graph/classdef-graph.hh
index c2e24a7067..4ae0c13acc 100644
--- a/thirdparty/harfbuzz/src/graph/classdef-graph.hh
+++ b/thirdparty/harfbuzz/src/graph/classdef-graph.hh
@@ -94,7 +94,13 @@ struct ClassDef : public OT::ClassDef
}
hb_bytes_t class_def_copy = serializer.copy_bytes ();
- c.add_buffer ((char *) class_def_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer.
+ if (!class_def_copy.arrayZ) return false;
+ // Give ownership to the context, it will cleanup the buffer.
+ if (!c.add_buffer ((char *) class_def_copy.arrayZ))
+ {
+ hb_free ((char *) class_def_copy.arrayZ);
+ return false;
+ }
auto& obj = c.graph.vertices_[dest_obj].obj;
obj.head = (char *) class_def_copy.arrayZ;
diff --git a/thirdparty/harfbuzz/src/graph/coverage-graph.hh b/thirdparty/harfbuzz/src/graph/coverage-graph.hh
index 49d0936315..bd6e91a1f2 100644
--- a/thirdparty/harfbuzz/src/graph/coverage-graph.hh
+++ b/thirdparty/harfbuzz/src/graph/coverage-graph.hh
@@ -118,7 +118,13 @@ struct Coverage : public OT::Layout::Common::Coverage
}
hb_bytes_t coverage_copy = serializer.copy_bytes ();
- c.add_buffer ((char *) coverage_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer.
+ if (!coverage_copy.arrayZ) return false;
+ // Give ownership to the context, it will cleanup the buffer.
+ if (!c.add_buffer ((char *) coverage_copy.arrayZ))
+ {
+ hb_free ((char *) coverage_copy.arrayZ);
+ return false;
+ }
auto& obj = c.graph.vertices_[dest_obj].obj;
obj.head = (char *) coverage_copy.arrayZ;
diff --git a/thirdparty/harfbuzz/src/graph/graph.hh b/thirdparty/harfbuzz/src/graph/graph.hh
index 294a999918..53d0bc94e1 100644
--- a/thirdparty/harfbuzz/src/graph/graph.hh
+++ b/thirdparty/harfbuzz/src/graph/graph.hh
@@ -359,7 +359,6 @@ struct graph_t
~graph_t ()
{
- vertices_.fini ();
for (char* b : buffers)
hb_free (b);
}
@@ -401,9 +400,10 @@ struct graph_t
return vertices_[i].obj;
}
- void add_buffer (char* buffer)
+ bool add_buffer (char* buffer)
{
buffers.push (buffer);
+ return !buffers.in_error ();
}
/*
@@ -732,8 +732,7 @@ struct graph_t
remap_obj_indices (index_map, parents.iter (), true);
// Update roots set with new indices as needed.
- uint32_t next = HB_SET_VALUE_INVALID;
- while (roots.next (&next))
+ for (auto next : roots)
{
const uint32_t *v;
if (index_map.has (next, &v))
diff --git a/thirdparty/harfbuzz/src/graph/gsubgpos-context.cc b/thirdparty/harfbuzz/src/graph/gsubgpos-context.cc
index b2044426d4..d66eb49cfd 100644
--- a/thirdparty/harfbuzz/src/graph/gsubgpos-context.cc
+++ b/thirdparty/harfbuzz/src/graph/gsubgpos-context.cc
@@ -52,7 +52,11 @@ unsigned gsubgpos_graph_context_t::create_node (unsigned size)
if (!buffer)
return -1;
- add_buffer (buffer);
+ if (!add_buffer (buffer)) {
+ // Allocation did not get stored for freeing later.
+ hb_free (buffer);
+ return -1;
+ }
return graph.new_node (buffer, buffer + size);
}
diff --git a/thirdparty/harfbuzz/src/graph/gsubgpos-context.hh b/thirdparty/harfbuzz/src/graph/gsubgpos-context.hh
index 9fe9662e64..26b7cfe4d4 100644
--- a/thirdparty/harfbuzz/src/graph/gsubgpos-context.hh
+++ b/thirdparty/harfbuzz/src/graph/gsubgpos-context.hh
@@ -47,9 +47,9 @@ struct gsubgpos_graph_context_t
HB_INTERNAL unsigned create_node (unsigned size);
- void add_buffer (char* buffer)
+ bool add_buffer (char* buffer)
{
- graph.add_buffer (buffer);
+ return graph.add_buffer (buffer);
}
private:
diff --git a/thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh b/thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh
index c170638409..78d5096325 100644
--- a/thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh
+++ b/thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh
@@ -166,7 +166,7 @@ struct Lookup : public OT::Lookup
}
if (all_new_subtables) {
- add_sub_tables (c, this_index, type, all_new_subtables);
+ return add_sub_tables (c, this_index, type, all_new_subtables);
}
return true;
@@ -184,7 +184,7 @@ struct Lookup : public OT::Lookup
return sub_table->split_subtables (c, parent_idx, objidx);
}
- void add_sub_tables (gsubgpos_graph_context_t& c,
+ bool add_sub_tables (gsubgpos_graph_context_t& c,
unsigned this_index,
unsigned type,
hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids)
@@ -200,7 +200,12 @@ struct Lookup : public OT::Lookup
size_t new_size = v.table_size ()
+ new_subtable_count * OT::Offset16::static_size;
char* buffer = (char*) hb_calloc (1, new_size);
- c.add_buffer (buffer);
+ if (!buffer) return false;
+ if (!c.add_buffer (buffer))
+ {
+ hb_free (buffer);
+ return false;
+ }
hb_memcpy (buffer, v.obj.head, v.table_size());
v.obj.head = buffer;
@@ -239,6 +244,7 @@ struct Lookup : public OT::Lookup
// The head location of the lookup has changed, invalidating the lookups map entry
// in the context. Update the map.
c.lookups.set (this_index, new_lookup);
+ return true;
}
void fix_existing_subtable_links (gsubgpos_graph_context_t& c,
diff --git a/thirdparty/harfbuzz/src/graph/pairpos-graph.hh b/thirdparty/harfbuzz/src/graph/pairpos-graph.hh
index 1c13eb24f9..f655b71558 100644
--- a/thirdparty/harfbuzz/src/graph/pairpos-graph.hh
+++ b/thirdparty/harfbuzz/src/graph/pairpos-graph.hh
@@ -215,7 +215,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
auto gid_and_class =
+ coverage->iter ()
| hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1->get_class (gid));
+ return hb_codepoint_pair_t (gid, class_def_1->get_class (gid));
})
;
class_def_size_estimator_t estimator (gid_and_class);
@@ -386,14 +386,14 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
auto klass_map =
+ coverage_table->iter ()
| hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1_table->get_class (gid));
+ return hb_codepoint_pair_t (gid, class_def_1_table->get_class (gid));
})
| hb_filter ([&] (hb_codepoint_t klass) {
return klass >= start && klass < end;
}, hb_second)
- | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, hb_codepoint_t> gid_and_class) {
+ | hb_map_retains_sorting ([&] (hb_codepoint_pair_t gid_and_class) {
// Classes must be from 0...N so subtract start
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid_and_class.first, gid_and_class.second - start);
+ return hb_codepoint_pair_t (gid_and_class.first, gid_and_class.second - start);
})
;
@@ -519,7 +519,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
auto klass_map =
+ coverage.table->iter ()
| hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1.table->get_class (gid));
+ return hb_codepoint_pair_t (gid, class_def_1.table->get_class (gid));
})
| hb_filter ([&] (hb_codepoint_t klass) {
return klass < count;
diff --git a/thirdparty/harfbuzz/src/graph/serialize.hh b/thirdparty/harfbuzz/src/graph/serialize.hh
index 2e0b845baa..06e4bf44d8 100644
--- a/thirdparty/harfbuzz/src/graph/serialize.hh
+++ b/thirdparty/harfbuzz/src/graph/serialize.hh
@@ -226,6 +226,9 @@ inline hb_blob_t* serialize (const graph_t& graph)
{
hb_vector_t<char> buffer;
size_t size = graph.total_size_in_bytes ();
+
+ if (!size) return hb_blob_get_empty ();
+
if (!buffer.alloc (size)) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer.");
return nullptr;
diff --git a/thirdparty/harfbuzz/src/hb-algs.hh b/thirdparty/harfbuzz/src/hb-algs.hh
index da383e050a..374965d56e 100644
--- a/thirdparty/harfbuzz/src/hb-algs.hh
+++ b/thirdparty/harfbuzz/src/hb-algs.hh
@@ -87,6 +87,19 @@ static inline constexpr uint16_t hb_uint16_swap (uint16_t v)
static inline constexpr uint32_t hb_uint32_swap (uint32_t v)
{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
+#ifndef HB_FAST_INT_ACCESS
+#if defined(__OPTIMIZE__) && \
+ defined(__BYTE_ORDER) && \
+ (__BYTE_ORDER == __BIG_ENDIAN || \
+ (__BYTE_ORDER == __LITTLE_ENDIAN && \
+ hb_has_builtin(__builtin_bswap16) && \
+ hb_has_builtin(__builtin_bswap32)))
+#define HB_FAST_INT_ACCESS 1
+#else
+#define HB_FAST_INT_ACCESS 0
+#endif
+#endif
+
template <typename Type, int Bytes = sizeof (Type)>
struct BEInt;
template <typename Type>
@@ -101,21 +114,25 @@ struct BEInt<Type, 1>
template <typename Type>
struct BEInt<Type, 2>
{
+ struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
+
public:
BEInt () = default;
- constexpr BEInt (Type V) : v {uint8_t ((V >> 8) & 0xFF),
- uint8_t ((V ) & 0xFF)} {}
- struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
- constexpr operator Type () const
- {
-#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
- defined(__BYTE_ORDER) && \
- (__BYTE_ORDER == __BIG_ENDIAN || \
- (__BYTE_ORDER == __LITTLE_ENDIAN && \
- hb_has_builtin(__builtin_bswap16)))
- /* Spoon-feed the compiler a big-endian integer with alignment 1.
- * https://github.com/harfbuzz/harfbuzz/pull/1398 */
+ BEInt (Type V)
+#if HB_FAST_INT_ACCESS
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ { ((packed_uint16_t *) v)->v = __builtin_bswap16 (V); }
+#else /* __BYTE_ORDER == __BIG_ENDIAN */
+ { ((packed_uint16_t *) v)->v = V; }
+#endif
+#else
+ : v {uint8_t ((V >> 8) & 0xFF),
+ uint8_t ((V ) & 0xFF)} {}
+#endif
+
+ constexpr operator Type () const {
+#if HB_FAST_INT_ACCESS
#if __BYTE_ORDER == __LITTLE_ENDIAN
return __builtin_bswap16 (((packed_uint16_t *) v)->v);
#else /* __BYTE_ORDER == __BIG_ENDIAN */
@@ -146,22 +163,27 @@ struct BEInt<Type, 3>
template <typename Type>
struct BEInt<Type, 4>
{
+ struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
+
public:
BEInt () = default;
- constexpr BEInt (Type V) : v {uint8_t ((V >> 24) & 0xFF),
- uint8_t ((V >> 16) & 0xFF),
- uint8_t ((V >> 8) & 0xFF),
- uint8_t ((V ) & 0xFF)} {}
- struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
+ BEInt (Type V)
+#if HB_FAST_INT_ACCESS
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ { ((packed_uint32_t *) v)->v = __builtin_bswap32 (V); }
+#else /* __BYTE_ORDER == __BIG_ENDIAN */
+ { ((packed_uint32_t *) v)->v = V; }
+#endif
+#else
+ : v {uint8_t ((V >> 24) & 0xFF),
+ uint8_t ((V >> 16) & 0xFF),
+ uint8_t ((V >> 8) & 0xFF),
+ uint8_t ((V ) & 0xFF)} {}
+#endif
+
constexpr operator Type () const {
-#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
- defined(__BYTE_ORDER) && \
- (__BYTE_ORDER == __BIG_ENDIAN || \
- (__BYTE_ORDER == __LITTLE_ENDIAN && \
- hb_has_builtin(__builtin_bswap32)))
- /* Spoon-feed the compiler a big-endian integer with alignment 1.
- * https://github.com/harfbuzz/harfbuzz/pull/1398 */
+#if HB_FAST_INT_ACCESS
#if __BYTE_ORDER == __LITTLE_ENDIAN
return __builtin_bswap32 (((packed_uint32_t *) v)->v);
#else /* __BYTE_ORDER == __BIG_ENDIAN */
@@ -231,12 +253,119 @@ struct
}
HB_FUNCOBJ (hb_bool);
+
+/* The MIT License
+
+ Copyright (C) 2012 Zilong Tan (eric.zltan@gmail.com)
+
+ 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.
+*/
+
+
+// Compression function for Merkle-Damgard construction.
+// This function is generated using the framework provided.
+#define mix(h) ( \
+ (h) ^= (h) >> 23, \
+ (h) *= 0x2127599bf4325c37ULL, \
+ (h) ^= (h) >> 47)
+
+static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
+{
+ struct __attribute__((packed)) packed_uint64_t { uint64_t v; };
+ const uint64_t m = 0x880355f21e6d1965ULL;
+ const packed_uint64_t *pos = (const packed_uint64_t *)buf;
+ const packed_uint64_t *end = pos + (len / 8);
+ const unsigned char *pos2;
+ uint64_t h = seed ^ (len * m);
+ uint64_t v;
+
+#ifndef HB_OPTIMIZE_SIZE
+ if (((uintptr_t) pos & 7) == 0)
+ {
+ while (pos != end)
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ v = * (const uint64_t *) (pos++);
+#pragma GCC diagnostic pop
+ h ^= mix(v);
+ h *= m;
+ }
+ }
+ else
+#endif
+ {
+ while (pos != end)
+ {
+ v = pos++->v;
+ h ^= mix(v);
+ h *= m;
+ }
+ }
+
+ pos2 = (const unsigned char*)pos;
+ v = 0;
+
+ switch (len & 7) {
+ case 7: v ^= (uint64_t)pos2[6] << 48; HB_FALLTHROUGH;
+ case 6: v ^= (uint64_t)pos2[5] << 40; HB_FALLTHROUGH;
+ case 5: v ^= (uint64_t)pos2[4] << 32; HB_FALLTHROUGH;
+ case 4: v ^= (uint64_t)pos2[3] << 24; HB_FALLTHROUGH;
+ case 3: v ^= (uint64_t)pos2[2] << 16; HB_FALLTHROUGH;
+ case 2: v ^= (uint64_t)pos2[1] << 8; HB_FALLTHROUGH;
+ case 1: v ^= (uint64_t)pos2[0];
+ h ^= mix(v);
+ h *= m;
+ }
+
+ return mix(h);
+}
+
+static inline uint32_t fasthash32(const void *buf, size_t len, uint32_t seed)
+{
+ // the following trick converts the 64-bit hashcode to Fermat
+ // residue, which shall retain information from both the higher
+ // and lower parts of hashcode.
+ uint64_t h = fasthash64(buf, len, seed);
+ return h - (h >> 32);
+}
+
struct
{
private:
template <typename T> constexpr auto
- impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
+ impl (const T& v, hb_priority<2>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
+
+ // Horrible: std:hash() of integers seems to be identity in gcc / clang?!
+ // https://github.com/harfbuzz/harfbuzz/pull/4228
+ //
+ // For performance characteristics see:
+ // https://github.com/harfbuzz/harfbuzz/pull/4228#issuecomment-1565079537
+ template <typename T,
+ hb_enable_if (std::is_integral<T>::value && sizeof (T) <= sizeof (uint32_t))> constexpr auto
+ impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, v * 2654435761u /* Knuh's multiplicative hash */)
+ template <typename T,
+ hb_enable_if (std::is_integral<T>::value && sizeof (T) > sizeof (uint32_t))> constexpr auto
+ impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, (v ^ (v >> 32)) * 2654435761u /* Knuth's multiplicative hash */)
template <typename T> constexpr auto
impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v))>>{} (hb_deref (v)))
@@ -551,6 +680,8 @@ struct hb_pair_t
template <typename T1, typename T2> static inline hb_pair_t<T1, T2>
hb_pair (T1&& a, T2&& b) { return hb_pair_t<T1, T2> (a, b); }
+typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t;
+
struct
{
template <typename Pair> constexpr typename Pair::first_t
@@ -853,7 +984,7 @@ static inline void *
hb_memset (void *s, int c, unsigned int n)
{
/* It's illegal to pass NULL to memset(), even if n is zero. */
- if (unlikely (!n)) return 0;
+ if (unlikely (!n)) return s;
return memset (s, c, n);
}
diff --git a/thirdparty/harfbuzz/src/hb-array.hh b/thirdparty/harfbuzz/src/hb-array.hh
index 1a22e15c0f..760f90259c 100644
--- a/thirdparty/harfbuzz/src/hb-array.hh
+++ b/thirdparty/harfbuzz/src/hb-array.hh
@@ -75,11 +75,25 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
*/
typedef Type& __item_t__;
static constexpr bool is_random_access_iterator = true;
+ static constexpr bool has_fast_len = true;
+ Type& __item__ () const
+ {
+ if (unlikely (!length)) return CrapOrNull (Type);
+ return *arrayZ;
+ }
Type& __item_at__ (unsigned i) const
{
if (unlikely (i >= length)) return CrapOrNull (Type);
return arrayZ[i];
}
+ void __next__ ()
+ {
+ if (unlikely (!length))
+ return;
+ length--;
+ backwards_length++;
+ arrayZ++;
+ }
void __forward__ (unsigned n)
{
if (unlikely (n > length))
@@ -88,6 +102,14 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
backwards_length += n;
arrayZ += n;
}
+ void __prev__ ()
+ {
+ if (unlikely (!backwards_length))
+ return;
+ length++;
+ backwards_length--;
+ arrayZ--;
+ }
void __rewind__ (unsigned n)
{
if (unlikely (n > backwards_length))
@@ -123,6 +145,7 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
uint32_t hash () const
{
// FNV-1a hash function
+ // https://github.com/harfbuzz/harfbuzz/pull/4228
uint32_t current = /*cbf29ce4*/0x84222325;
for (auto &v : *this)
{
@@ -326,6 +349,7 @@ struct hb_sorted_array_t :
HB_ITER_USING (iter_base_t);
static constexpr bool is_random_access_iterator = true;
static constexpr bool is_sorted_iterator = true;
+ static constexpr bool has_fast_len = true;
hb_sorted_array_t () = default;
hb_sorted_array_t (const hb_sorted_array_t&) = default;
@@ -453,55 +477,21 @@ inline bool hb_array_t<const unsigned char>::operator == (const hb_array_t<const
/* Specialize hash() for byte arrays. */
+#ifndef HB_OPTIMIZE_SIZE_MORE
template <>
inline uint32_t hb_array_t<const char>::hash () const
{
- // FNV-1a hash function
- uint32_t current = /*cbf29ce4*/0x84222325;
- unsigned i = 0;
-
-#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
- ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__))
- struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
- for (; i + 4 <= this->length; i += 4)
- {
- current = current ^ hb_hash ((uint32_t) ((const packed_uint32_t *) &this->arrayZ[i])->v);
- current = current * 16777619;
- }
-#endif
-
- for (; i < this->length; i++)
- {
- current = current ^ hb_hash (this->arrayZ[i]);
- current = current * 16777619;
- }
- return current;
+ // https://github.com/harfbuzz/harfbuzz/pull/4228
+ return fasthash32(arrayZ, length, 0xf437ffe6 /* magic? */);
}
template <>
inline uint32_t hb_array_t<const unsigned char>::hash () const
{
- // FNV-1a hash function
- uint32_t current = /*cbf29ce4*/0x84222325;
- unsigned i = 0;
-
-#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
- ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__))
- struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
- for (; i + 4 <= this->length; i += 4)
- {
- current = current ^ hb_hash ((uint32_t) ((const packed_uint32_t *) &this->arrayZ[i])->v);
- current = current * 16777619;
- }
-#endif
-
- for (; i < this->length; i++)
- {
- current = current ^ hb_hash (this->arrayZ[i]);
- current = current * 16777619;
- }
- return current;
+ // https://github.com/harfbuzz/harfbuzz/pull/4228
+ return fasthash32(arrayZ, length, 0xf437ffe6 /* magic? */);
}
+#endif
typedef hb_array_t<const char> hb_bytes_t;
diff --git a/thirdparty/harfbuzz/src/hb-atomic.hh b/thirdparty/harfbuzz/src/hb-atomic.hh
index a6283de140..303dfe6d04 100644
--- a/thirdparty/harfbuzz/src/hb-atomic.hh
+++ b/thirdparty/harfbuzz/src/hb-atomic.hh
@@ -204,6 +204,7 @@ struct hb_atomic_ptr_t
hb_atomic_ptr_t () = default;
constexpr hb_atomic_ptr_t (T* v) : v (v) {}
+ hb_atomic_ptr_t (const hb_atomic_ptr_t &other) = delete;
void init (T* v_ = nullptr) { set_relaxed (v_); }
void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); }
diff --git a/thirdparty/harfbuzz/src/hb-bimap.hh b/thirdparty/harfbuzz/src/hb-bimap.hh
index 9edefd9710..4006fc4ebd 100644
--- a/thirdparty/harfbuzz/src/hb-bimap.hh
+++ b/thirdparty/harfbuzz/src/hb-bimap.hh
@@ -39,10 +39,10 @@ struct hb_bimap_t
back_map.reset ();
}
- void resize (unsigned pop)
+ void alloc (unsigned pop)
{
- forw_map.resize (pop);
- back_map.resize (pop);
+ forw_map.alloc (pop);
+ back_map.alloc (pop);
}
bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
@@ -83,7 +83,6 @@ struct hb_bimap_t
unsigned int get_population () const { return forw_map.get_population (); }
-
protected:
hb_map_t forw_map;
hb_map_t back_map;
@@ -95,8 +94,30 @@ struct hb_bimap_t
};
/* Inremental bimap: only lhs is given, rhs is incrementally assigned */
-struct hb_inc_bimap_t : hb_bimap_t
+struct hb_inc_bimap_t
{
+ bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
+
+ unsigned int get_population () const { return forw_map.get_population (); }
+
+ void reset ()
+ {
+ forw_map.reset ();
+ back_map.reset ();
+ }
+
+ void alloc (unsigned pop)
+ {
+ forw_map.alloc (pop);
+ back_map.alloc (pop);
+ }
+
+ void clear ()
+ {
+ forw_map.clear ();
+ back_map.resize (0);
+ }
+
/* Add a mapping from lhs to rhs with a unique value if lhs is unknown.
* Return the rhs value as the result.
*/
@@ -105,32 +126,41 @@ struct hb_inc_bimap_t : hb_bimap_t
hb_codepoint_t rhs = forw_map[lhs];
if (rhs == HB_MAP_VALUE_INVALID)
{
- rhs = next_value++;
- set (lhs, rhs);
+ rhs = back_map.length;
+ forw_map.set (lhs, rhs);
+ back_map.push (lhs);
}
return rhs;
}
hb_codepoint_t skip ()
- { return next_value++; }
+ {
+ hb_codepoint_t start = back_map.length;
+ back_map.push (HB_MAP_VALUE_INVALID);
+ return start;
+ }
hb_codepoint_t skip (unsigned count)
- { return next_value += count; }
+ {
+ hb_codepoint_t start = back_map.length;
+ for (unsigned i = 0; i < count; i++)
+ back_map.push (HB_MAP_VALUE_INVALID);
+ return start;
+ }
hb_codepoint_t get_next_value () const
- { return next_value; }
+ { return back_map.length; }
void add_set (const hb_set_t *set)
{
- hb_codepoint_t i = HB_SET_VALUE_INVALID;
- while (hb_set_next (set, &i)) add (i);
+ for (auto i : *set) add (i);
}
/* Create an identity map. */
bool identity (unsigned int size)
{
clear ();
- for (hb_codepoint_t i = 0; i < size; i++) set (i, i);
+ for (hb_codepoint_t i = 0; i < size; i++) add (i);
return !in_error ();
}
@@ -145,20 +175,30 @@ struct hb_inc_bimap_t : hb_bimap_t
{
hb_codepoint_t count = get_population ();
hb_vector_t <hb_codepoint_t> work;
- work.resize (count);
+ if (unlikely (!work.resize (count, false))) return;
for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
- work[rhs] = back_map[rhs];
+ work.arrayZ[rhs] = back_map[rhs];
work.qsort (cmp_id);
clear ();
for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
- set (work[rhs], rhs);
+ add (work.arrayZ[rhs]);
}
+ hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); }
+ hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map[rhs]; }
+
+ hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); }
+ bool has (hb_codepoint_t lhs) const { return forw_map.has (lhs); }
+
protected:
- unsigned int next_value = 0;
+ hb_map_t forw_map;
+ hb_vector_t<hb_codepoint_t> back_map;
+
+ public:
+ auto keys () const HB_AUTO_RETURN (+ back_map.iter())
};
#endif /* HB_BIMAP_HH */
diff --git a/thirdparty/harfbuzz/src/hb-bit-page.hh b/thirdparty/harfbuzz/src/hb-bit-page.hh
index 9b027ac590..e578d2643f 100644
--- a/thirdparty/harfbuzz/src/hb-bit-page.hh
+++ b/thirdparty/harfbuzz/src/hb-bit-page.hh
@@ -104,10 +104,7 @@ struct hb_bit_page_t
}
uint32_t hash () const
{
- return
- + hb_iter (v)
- | hb_reduce ([] (uint32_t h, const elt_t &_) { return h * 31 + hb_hash (_); }, (uint32_t) 0u)
- ;
+ return hb_bytes_t ((const char *) &v, sizeof (v)).hash ();
}
void add (hb_codepoint_t g) { elt (g) |= mask (g); }
diff --git a/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh b/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh
index 1eb1b1c209..e765a479ae 100644
--- a/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh
+++ b/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh
@@ -136,7 +136,7 @@ struct hb_bit_set_invertible_t
/* Sink interface. */
hb_bit_set_invertible_t& operator << (hb_codepoint_t v)
{ add (v); return *this; }
- hb_bit_set_invertible_t& operator << (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& range)
+ hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
@@ -162,7 +162,7 @@ struct hb_bit_set_invertible_t
auto it1 = iter ();
auto it2 = other.iter ();
return hb_all (+ hb_zip (it1, it2)
- | hb_map ([](hb_pair_t<hb_codepoint_t, hb_codepoint_t> _) { return _.first == _.second; }));
+ | hb_map ([](hb_codepoint_pair_t _) { return _.first == _.second; }));
}
}
@@ -345,6 +345,7 @@ struct hb_bit_set_invertible_t
struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
{
static constexpr bool is_sorted_iterator = true;
+ static constexpr bool has_fast_len = true;
iter_t (const hb_bit_set_invertible_t &s_ = Null (hb_bit_set_invertible_t),
bool init = true) : s (&s_), v (INVALID), l(0)
{
@@ -363,7 +364,7 @@ struct hb_bit_set_invertible_t
unsigned __len__ () const { return l; }
iter_t end () const { return iter_t (*s, false); }
bool operator != (const iter_t& o) const
- { return s != o.s || v != o.v; }
+ { return v != o.v || s != o.s; }
protected:
const hb_bit_set_invertible_t *s;
diff --git a/thirdparty/harfbuzz/src/hb-bit-set.hh b/thirdparty/harfbuzz/src/hb-bit-set.hh
index d290f6114c..a84d751fb3 100644
--- a/thirdparty/harfbuzz/src/hb-bit-set.hh
+++ b/thirdparty/harfbuzz/src/hb-bit-set.hh
@@ -134,7 +134,11 @@ struct hb_bit_set_t
{
uint32_t h = 0;
for (auto &map : page_map)
- h = h * 31 + hb_hash (map.major) + hb_hash (pages[map.index]);
+ {
+ auto &page = pages.arrayZ[map.index];
+ if (unlikely (page.is_empty ())) continue;
+ h = h * 31 + hb_hash (map.major) + hb_hash (page);
+ }
return h;
}
@@ -342,7 +346,7 @@ struct hb_bit_set_t
/* Sink interface. */
hb_bit_set_t& operator << (hb_codepoint_t v)
{ add (v); return *this; }
- hb_bit_set_t& operator << (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& range)
+ hb_bit_set_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
@@ -862,6 +866,7 @@ struct hb_bit_set_t
struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
{
static constexpr bool is_sorted_iterator = true;
+ static constexpr bool has_fast_len = true;
iter_t (const hb_bit_set_t &s_ = Null (hb_bit_set_t),
bool init = true) : s (&s_), v (INVALID), l(0)
{
diff --git a/thirdparty/harfbuzz/src/hb-buffer-verify.cc b/thirdparty/harfbuzz/src/hb-buffer-verify.cc
index f111b2d8dc..15a53919de 100644
--- a/thirdparty/harfbuzz/src/hb-buffer-verify.cc
+++ b/thirdparty/harfbuzz/src/hb-buffer-verify.cc
@@ -162,14 +162,8 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer,
hb_buffer_set_flags (fragment, flags);
hb_buffer_append (fragment, text_buffer, text_start, text_end);
- if (!hb_shape_full (font, fragment, features, num_features, shapers))
- {
- buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment.");
- hb_buffer_destroy (reconstruction);
- hb_buffer_destroy (fragment);
- return false;
- }
- else if (!fragment->successful || fragment->shaping_failed)
+ if (!hb_shape_full (font, fragment, features, num_features, shapers) ||
+ fragment->successful || fragment->shaping_failed)
{
hb_buffer_destroy (reconstruction);
hb_buffer_destroy (fragment);
@@ -185,15 +179,18 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer,
}
bool ret = true;
- hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
- if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH)
+ if (likely (reconstruction->successful))
{
- buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break test failed.");
- ret = false;
+ hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
+ if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH)
+ {
+ buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break test failed.");
+ ret = false;
- /* Return the reconstructed result instead so it can be inspected. */
- hb_buffer_set_length (buffer, 0);
- hb_buffer_append (buffer, reconstruction, 0, -1);
+ /* Return the reconstructed result instead so it can be inspected. */
+ hb_buffer_set_length (buffer, 0);
+ hb_buffer_append (buffer, reconstruction, 0, -1);
+ }
}
hb_buffer_destroy (reconstruction);
@@ -316,28 +313,13 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer,
/*
* Shape the two fragment streams.
*/
- if (!hb_shape_full (font, fragments[0], features, num_features, shapers))
- {
- buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment.");
- ret = false;
- goto out;
- }
- else if (!fragments[0]->successful || fragments[0]->shaping_failed)
- {
- ret = true;
- goto out;
- }
- if (!hb_shape_full (font, fragments[1], features, num_features, shapers))
- {
- buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment.");
- ret = false;
+ if (!hb_shape_full (font, fragments[0], features, num_features, shapers) ||
+ !fragments[0]->successful || fragments[0]->shaping_failed)
goto out;
- }
- else if (!fragments[1]->successful || fragments[1]->shaping_failed)
- {
- ret = true;
+
+ if (!hb_shape_full (font, fragments[1], features, num_features, shapers) ||
+ !fragments[1]->successful || fragments[1]->shaping_failed)
goto out;
- }
if (!forward)
{
@@ -377,21 +359,23 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer,
hb_buffer_reverse (reconstruction);
}
- /*
- * Diff results.
- */
- diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
- if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH)
+ if (likely (reconstruction->successful))
{
- buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-concat test failed.");
- ret = false;
+ /*
+ * Diff results.
+ */
+ diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
+ if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH)
+ {
+ buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-concat test failed.");
+ ret = false;
- /* Return the reconstructed result instead so it can be inspected. */
- hb_buffer_set_length (buffer, 0);
- hb_buffer_append (buffer, reconstruction, 0, -1);
+ /* Return the reconstructed result instead so it can be inspected. */
+ hb_buffer_set_length (buffer, 0);
+ hb_buffer_append (buffer, reconstruction, 0, -1);
+ }
}
-
out:
hb_buffer_destroy (reconstruction);
hb_buffer_destroy (fragments[0]);
diff --git a/thirdparty/harfbuzz/src/hb-buffer.hh b/thirdparty/harfbuzz/src/hb-buffer.hh
index 7a97fc7168..4d48b7f167 100644
--- a/thirdparty/harfbuzz/src/hb-buffer.hh
+++ b/thirdparty/harfbuzz/src/hb-buffer.hh
@@ -493,6 +493,13 @@ struct hb_buffer_t
HB_NODISCARD HB_INTERNAL bool enlarge (unsigned int size);
+ HB_NODISCARD bool resize (unsigned length)
+ {
+ assert (!have_output);
+ if (unlikely (!ensure (length))) return false;
+ len = length;
+ return true;
+ }
HB_NODISCARD bool ensure (unsigned int size)
{ return likely (!size || size < allocated) ? true : enlarge (size); }
diff --git a/thirdparty/harfbuzz/src/hb-cache.hh b/thirdparty/harfbuzz/src/hb-cache.hh
index 8371465c6c..6d8a54cf10 100644
--- a/thirdparty/harfbuzz/src/hb-cache.hh
+++ b/thirdparty/harfbuzz/src/hb-cache.hh
@@ -62,14 +62,12 @@ struct hb_cache_t
static_assert ((key_bits >= cache_bits), "");
static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), "");
- hb_cache_t () { init (); }
-
- void init () { clear (); }
+ hb_cache_t () { clear (); }
void clear ()
{
- for (unsigned i = 0; i < ARRAY_LENGTH (values); i++)
- values[i] = -1;
+ for (auto &v : values)
+ v = -1;
}
bool get (unsigned int key, unsigned int *value) const
diff --git a/thirdparty/harfbuzz/src/hb-cairo-utils.cc b/thirdparty/harfbuzz/src/hb-cairo-utils.cc
index 0f94d8169f..ec1499e861 100644
--- a/thirdparty/harfbuzz/src/hb-cairo-utils.cc
+++ b/thirdparty/harfbuzz/src/hb-cairo-utils.cc
@@ -80,7 +80,7 @@ hb_cairo_read_blob (void *closure,
if (r->offset + length > size)
return CAIRO_STATUS_READ_ERROR;
- memcpy (data, d + r->offset, length);
+ hb_memcpy (data, d + r->offset, length);
r->offset += length;
return CAIRO_STATUS_SUCCESS;
@@ -763,7 +763,7 @@ _hb_cairo_add_sweep_gradient_patches (hb_color_stop_t *stops,
}
//assert (angles[0] + k * span <= 0 && 0 < angles[n_stops - 1] + k * span);
- span = fabs (span);
+ span = fabsf (span);
for (signed l = k; l < 1000; l++)
{
diff --git a/thirdparty/harfbuzz/src/hb-cairo.cc b/thirdparty/harfbuzz/src/hb-cairo.cc
index f005afd17e..68c7bc064f 100644
--- a/thirdparty/harfbuzz/src/hb-cairo.cc
+++ b/thirdparty/harfbuzz/src/hb-cairo.cc
@@ -956,7 +956,7 @@ hb_cairo_glyphs_from_buffer (hb_buffer_t *buffer,
if (clusters && *num_clusters && utf8)
{
- memset ((void *) *clusters, 0, *num_clusters * sizeof ((*clusters)[0]));
+ hb_memset ((void *) *clusters, 0, *num_clusters * sizeof ((*clusters)[0]));
hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
*cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
unsigned int cluster = 0;
diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh
index 949bfebf9b..1d1f10f2bf 100644
--- a/thirdparty/harfbuzz/src/hb-cff-interp-common.hh
+++ b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh
@@ -26,6 +26,8 @@
#ifndef HB_CFF_INTERP_COMMON_HH
#define HB_CFF_INTERP_COMMON_HH
+extern HB_INTERNAL const unsigned char *endchar_str;
+
namespace CFF {
using namespace OT;
@@ -336,8 +338,6 @@ struct byte_str_ref_t
hb_ubytes_t str;
};
-using byte_str_array_t = hb_vector_t<hb_ubytes_t>;
-
/* stack */
template <typename ELEM, int LIMIT>
struct cff_stack_t
diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh
index f40be51f0d..28a777eb0d 100644
--- a/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh
+++ b/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh
@@ -883,14 +883,12 @@ struct cs_interpreter_t : interpreter_t<ENV>
unsigned max_ops = HB_CFF_MAX_OPS;
for (;;) {
- if (unlikely (!--max_ops))
+ OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
+ if (unlikely (SUPER::env.in_error () || !--max_ops))
{
SUPER::env.set_error ();
- break;
- }
- OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
- if (unlikely (SUPER::env.in_error ()))
return false;
+ }
if (SUPER::env.is_endchar ())
break;
}
diff --git a/thirdparty/harfbuzz/src/hb-common.h b/thirdparty/harfbuzz/src/hb-common.h
index a5da4e76a3..a9fe666b39 100644
--- a/thirdparty/harfbuzz/src/hb-common.h
+++ b/thirdparty/harfbuzz/src/hb-common.h
@@ -104,6 +104,16 @@ typedef int hb_bool_t;
*
**/
typedef uint32_t hb_codepoint_t;
+
+/**
+ * HB_CODEPOINT_INVALID:
+ *
+ * Unused #hb_codepoint_t value.
+ *
+ * Since: 8.0.0
+ */
+#define HB_CODEPOINT_INVALID ((hb_codepoint_t) -1)
+
/**
* hb_position_t:
*
diff --git a/thirdparty/harfbuzz/src/hb-config.hh b/thirdparty/harfbuzz/src/hb-config.hh
index 26f7cba83e..335c6976f6 100644
--- a/thirdparty/harfbuzz/src/hb-config.hh
+++ b/thirdparty/harfbuzz/src/hb-config.hh
@@ -113,7 +113,6 @@
/* Closure of options. */
#ifdef HB_NO_BORING_EXPANSION
-#define HB_NO_AVAR2
#define HB_NO_BEYOND_64K
#define HB_NO_CUBIC_GLYF
#define HB_NO_VAR_COMPOSITES
diff --git a/thirdparty/harfbuzz/src/hb-debug.hh b/thirdparty/harfbuzz/src/hb-debug.hh
index 0ac4515fa8..6055fce962 100644
--- a/thirdparty/harfbuzz/src/hb-debug.hh
+++ b/thirdparty/harfbuzz/src/hb-debug.hh
@@ -389,6 +389,10 @@ struct hb_no_trace_t {
#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0)
#endif
+#ifndef HB_DEBUG_WASM
+#define HB_DEBUG_WASM (HB_DEBUG+0)
+#endif
+
/*
* With tracing.
*/
diff --git a/thirdparty/harfbuzz/src/hb-deprecated.h b/thirdparty/harfbuzz/src/hb-deprecated.h
index b032a941b2..9fcce6d9ac 100644
--- a/thirdparty/harfbuzz/src/hb-deprecated.h
+++ b/thirdparty/harfbuzz/src/hb-deprecated.h
@@ -255,6 +255,52 @@ HB_EXTERN hb_position_t
hb_font_get_glyph_v_kerning (hb_font_t *font,
hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph);
+
+/**
+ * hb_font_get_glyph_shape_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @glyph: The glyph ID to query
+ * @draw_funcs: The draw functions to send the shape data to
+ * @draw_data: The data accompanying the draw functions
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * Since: 4.0.0
+ * Deprecated: 7.0.0: Use #hb_font_draw_glyph_func_t instead
+ **/
+typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph,
+ hb_draw_funcs_t *draw_funcs, void *draw_data,
+ void *user_data);
+
+/**
+ * hb_font_funcs_set_glyph_shape_func:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
+ *
+ * Sets the implementation function for #hb_font_get_glyph_shape_func_t,
+ * which is the same as #hb_font_draw_glyph_func_t.
+ *
+ * Since: 4.0.0
+ * Deprecated: 7.0.0: Use hb_font_funcs_set_draw_glyph_func() instead
+ **/
+HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_func)
+HB_EXTERN void
+hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs,
+ hb_font_get_glyph_shape_func_t func,
+ void *user_data, hb_destroy_func_t destroy);
+
+HB_DEPRECATED_FOR (hb_font_draw_glyph)
+HB_EXTERN void
+hb_font_get_glyph_shape (hb_font_t *font,
+ hb_codepoint_t glyph,
+ hb_draw_funcs_t *dfuncs, void *draw_data);
+
+
#endif
diff --git a/thirdparty/harfbuzz/src/hb-draw.hh b/thirdparty/harfbuzz/src/hb-draw.hh
index 768f51a875..25dee1261e 100644
--- a/thirdparty/harfbuzz/src/hb-draw.hh
+++ b/thirdparty/harfbuzz/src/hb-draw.hh
@@ -93,50 +93,57 @@ struct hb_draw_funcs_t
!user_data ? nullptr : user_data->close_path); }
- void move_to (void *draw_data, hb_draw_state_t &st,
- float to_x, float to_y)
+ void
+ HB_ALWAYS_INLINE
+ move_to (void *draw_data, hb_draw_state_t &st,
+ float to_x, float to_y)
{
- if (st.path_open) close_path (draw_data, st);
+ if (unlikely (st.path_open)) close_path (draw_data, st);
st.current_x = to_x;
st.current_y = to_y;
}
- void line_to (void *draw_data, hb_draw_state_t &st,
- float to_x, float to_y)
+ void
+ HB_ALWAYS_INLINE
+ line_to (void *draw_data, hb_draw_state_t &st,
+ float to_x, float to_y)
{
- if (!st.path_open) start_path (draw_data, st);
+ if (unlikely (!st.path_open)) start_path (draw_data, st);
emit_line_to (draw_data, st, to_x, to_y);
st.current_x = to_x;
st.current_y = to_y;
}
void
+ HB_ALWAYS_INLINE
quadratic_to (void *draw_data, hb_draw_state_t &st,
float control_x, float control_y,
float to_x, float to_y)
{
- if (!st.path_open) start_path (draw_data, st);
+ if (unlikely (!st.path_open)) start_path (draw_data, st);
emit_quadratic_to (draw_data, st, control_x, control_y, to_x, to_y);
st.current_x = to_x;
st.current_y = to_y;
}
void
+ HB_ALWAYS_INLINE
cubic_to (void *draw_data, hb_draw_state_t &st,
float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y)
{
- if (!st.path_open) start_path (draw_data, st);
+ if (unlikely (!st.path_open)) start_path (draw_data, st);
emit_cubic_to (draw_data, st, control1_x, control1_y, control2_x, control2_y, to_x, to_y);
st.current_x = to_x;
st.current_y = to_y;
}
void
+ HB_ALWAYS_INLINE
close_path (void *draw_data, hb_draw_state_t &st)
{
- if (st.path_open)
+ if (likely (st.path_open))
{
if ((st.path_start_x != st.current_x) || (st.path_start_y != st.current_y))
emit_line_to (draw_data, st, st.path_start_x, st.path_start_y);
@@ -168,6 +175,7 @@ struct hb_draw_session_t
~hb_draw_session_t () { close_path (); }
+ HB_ALWAYS_INLINE
void move_to (float to_x, float to_y)
{
if (likely (not_slanted))
@@ -177,6 +185,7 @@ struct hb_draw_session_t
funcs->move_to (draw_data, st,
to_x + to_y * slant, to_y);
}
+ HB_ALWAYS_INLINE
void line_to (float to_x, float to_y)
{
if (likely (not_slanted))
@@ -187,6 +196,7 @@ struct hb_draw_session_t
to_x + to_y * slant, to_y);
}
void
+ HB_ALWAYS_INLINE
quadratic_to (float control_x, float control_y,
float to_x, float to_y)
{
@@ -200,6 +210,7 @@ struct hb_draw_session_t
to_x + to_y * slant, to_y);
}
void
+ HB_ALWAYS_INLINE
cubic_to (float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y)
@@ -215,6 +226,7 @@ struct hb_draw_session_t
control2_x + control2_y * slant, control2_y,
to_x + to_y * slant, to_y);
}
+ HB_ALWAYS_INLINE
void close_path ()
{
funcs->close_path (draw_data, st);
diff --git a/thirdparty/harfbuzz/src/hb-font.cc b/thirdparty/harfbuzz/src/hb-font.cc
index 688513112a..f062bfaf75 100644
--- a/thirdparty/harfbuzz/src/hb-font.cc
+++ b/thirdparty/harfbuzz/src/hb-font.cc
@@ -1389,6 +1389,7 @@ hb_font_get_glyph_from_name (hb_font_t *font,
return font->get_glyph_from_name (name, len, glyph);
}
+#ifndef HB_DISABLE_DEPRECATED
/**
* hb_font_get_glyph_shape:
* @font: #hb_font_t to work upon
@@ -1410,6 +1411,7 @@ hb_font_get_glyph_shape (hb_font_t *font,
{
hb_font_draw_glyph (font, glyph, dfuncs, draw_data);
}
+#endif
/**
* hb_font_draw_glyph:
@@ -2648,7 +2650,6 @@ hb_font_set_variations (hb_font_t *font,
if (axes[axis_index].axisTag == tag)
design_coords[axis_index] = v;
}
- font->face->table.avar->map_coords (normalized, coords_length);
hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized);
_hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
@@ -2720,8 +2721,6 @@ hb_font_set_variation (hb_font_t *font,
if (axes[axis_index].axisTag == tag)
design_coords[axis_index] = value;
- font->face->table.avar->map_coords (normalized, coords_length);
-
hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized);
_hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
@@ -3058,6 +3057,7 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
#endif
+#ifndef HB_DISABLE_DEPRECATED
void
hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs,
hb_font_get_glyph_shape_func_t func,
@@ -3066,3 +3066,4 @@ hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs,
{
hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy);
}
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-font.h b/thirdparty/harfbuzz/src/hb-font.h
index f3b589bd0d..3c2355af2d 100644
--- a/thirdparty/harfbuzz/src/hb-font.h
+++ b/thirdparty/harfbuzz/src/hb-font.h
@@ -486,25 +486,6 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *
void *user_data);
/**
- * hb_font_get_glyph_shape_func_t:
- * @font: #hb_font_t to work upon
- * @font_data: @font user data pointer
- * @glyph: The glyph ID to query
- * @draw_funcs: The draw functions to send the shape data to
- * @draw_data: The data accompanying the draw functions
- * @user_data: User data pointer passed by the caller
- *
- * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
- *
- * Since: 4.0.0
- * Deprecated: 7.0.0: Use #hb_font_draw_glyph_func_t instead
- **/
-typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data,
- hb_codepoint_t glyph,
- hb_draw_funcs_t *draw_funcs, void *draw_data,
- void *user_data);
-
-/**
* hb_font_draw_glyph_func_t:
* @font: #hb_font_t to work upon
* @font_data: @font user data pointer
@@ -804,32 +785,13 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs,
void *user_data, hb_destroy_func_t destroy);
/**
- * hb_font_funcs_set_glyph_shape_func:
- * @ffuncs: A font-function structure
- * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
- * @user_data: Data to pass to @func
- * @destroy: (nullable): The function to call when @user_data is not needed anymore
- *
- * Sets the implementation function for #hb_font_get_glyph_shape_func_t,
- * which is the same as #hb_font_draw_glyph_func_t.
- *
- * Since: 4.0.0
- * Deprecated: 7.0.0: Use hb_font_funcs_set_draw_glyph_func() instead
- **/
-HB_EXTERN void
-hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs,
- hb_font_get_glyph_shape_func_t func,
- void *user_data, hb_destroy_func_t destroy);
-
-/**
* hb_font_funcs_set_draw_glyph_func:
* @ffuncs: A font-function structure
* @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
* @user_data: Data to pass to @func
* @destroy: (nullable): The function to call when @user_data is not needed anymore
*
- * Sets the implementation function for #hb_font_draw_glyph_func_t,
- * which is the same as #hb_font_get_glyph_shape_func_t.
+ * Sets the implementation function for #hb_font_draw_glyph_func_t.
*
* Since: 7.0.0
**/
@@ -935,11 +897,6 @@ hb_font_get_glyph_from_name (hb_font_t *font,
hb_codepoint_t *glyph);
HB_EXTERN void
-hb_font_get_glyph_shape (hb_font_t *font,
- hb_codepoint_t glyph,
- hb_draw_funcs_t *dfuncs, void *draw_data);
-
-HB_EXTERN void
hb_font_draw_glyph (hb_font_t *font,
hb_codepoint_t glyph,
hb_draw_funcs_t *dfuncs, void *draw_data);
diff --git a/thirdparty/harfbuzz/src/hb-ft.cc b/thirdparty/harfbuzz/src/hb-ft.cc
index 1105862fbc..6ca3f85465 100644
--- a/thirdparty/harfbuzz/src/hb-ft.cc
+++ b/thirdparty/harfbuzz/src/hb-ft.cc
@@ -114,7 +114,7 @@ _hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref)
ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
ft_font->cached_serial = (unsigned) -1;
- ft_font->advance_cache.init ();
+ new (&ft_font->advance_cache) hb_ft_advance_cache_t;
return ft_font;
}
diff --git a/thirdparty/harfbuzz/src/hb-gobject-structs.cc b/thirdparty/harfbuzz/src/hb-gobject-structs.cc
index 332cc84888..d66de0b237 100644
--- a/thirdparty/harfbuzz/src/hb-gobject-structs.cc
+++ b/thirdparty/harfbuzz/src/hb-gobject-structs.cc
@@ -29,7 +29,7 @@
#ifdef HAVE_GOBJECT
-/**
+/*
* SECTION:hb-gobject
* @title: hb-gobject
* @short_description: GObject integration support
diff --git a/thirdparty/harfbuzz/src/hb-graphite2.cc b/thirdparty/harfbuzz/src/hb-graphite2.cc
index 9e068f8d84..7ea0386223 100644
--- a/thirdparty/harfbuzz/src/hb-graphite2.cc
+++ b/thirdparty/harfbuzz/src/hb-graphite2.cc
@@ -248,6 +248,21 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED,
gr_fref_set_feature_value (fref, features[i].value, feats);
}
+ hb_direction_t direction = buffer->props.direction;
+ hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script);
+ /* TODO vertical:
+ * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
+ * Ogham fonts are supposed to be implemented BTT or not. Need to research that
+ * first. */
+ if ((HB_DIRECTION_IS_HORIZONTAL (direction) &&
+ direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) ||
+ (HB_DIRECTION_IS_VERTICAL (direction) &&
+ direction != HB_DIRECTION_TTB))
+ {
+ hb_buffer_reverse_clusters (buffer);
+ direction = HB_DIRECTION_REVERSE (direction);
+ }
+
gr_segment *seg = nullptr;
const gr_slot *is;
unsigned int ci = 0, ic = 0;
@@ -261,21 +276,11 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED,
for (unsigned int i = 0; i < buffer->len; ++i)
chars[i] = buffer->info[i].codepoint;
- /* TODO ensure_native_direction. */
-
- hb_tag_t script_tag[HB_OT_MAX_TAGS_PER_SCRIPT];
- unsigned int count = HB_OT_MAX_TAGS_PER_SCRIPT;
- hb_ot_tags_from_script_and_language (hb_buffer_get_script (buffer),
- HB_LANGUAGE_INVALID,
- &count,
- script_tag,
- nullptr, nullptr);
-
seg = gr_make_seg (nullptr, grface,
- count ? script_tag[count - 1] : HB_OT_TAG_DEFAULT_SCRIPT,
+ HB_TAG_NONE, // https://github.com/harfbuzz/harfbuzz/issues/3439#issuecomment-1442650148
feats,
gr_utf32, chars, buffer->len,
- 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
+ 2 | (direction == HB_DIRECTION_RTL ? 1 : 0));
if (unlikely (!seg)) {
if (feats) gr_featureval_destroy (feats);
@@ -327,7 +332,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED,
float yscale = (float) font->y_scale / upem;
yscale *= yscale / xscale;
unsigned int curradv = 0;
- if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+ if (HB_DIRECTION_IS_BACKWARD (direction))
{
curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale;
clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv;
@@ -356,16 +361,17 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED,
c->num_chars = before - c->base_char;
c->base_glyph = ic;
c->num_glyphs = 0;
- if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+ if (HB_DIRECTION_IS_BACKWARD (direction))
{
c->advance = curradv - gr_slot_origin_X(is) * xscale;
curradv -= c->advance;
}
else
{
+ auto origin_X = gr_slot_origin_X (is) * xscale;
c->advance = 0;
- clusters[ci].advance += gr_slot_origin_X(is) * xscale - curradv;
- curradv += clusters[ci].advance;
+ clusters[ci].advance += origin_X - curradv;
+ curradv = origin_X;
}
ci++;
}
@@ -375,7 +381,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED,
clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
}
- if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+ if (HB_DIRECTION_IS_BACKWARD (direction))
clusters[ci].advance += curradv;
else
clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv;
@@ -397,7 +403,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED,
unsigned int currclus = UINT_MAX;
const hb_glyph_info_t *info = buffer->info;
hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, nullptr);
- if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+ if (!HB_DIRECTION_IS_BACKWARD (direction))
{
curradvx = 0;
for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is))
diff --git a/thirdparty/harfbuzz/src/hb-iter.hh b/thirdparty/harfbuzz/src/hb-iter.hh
index b123b2f27c..61e05180be 100644
--- a/thirdparty/harfbuzz/src/hb-iter.hh
+++ b/thirdparty/harfbuzz/src/hb-iter.hh
@@ -63,6 +63,7 @@ struct hb_iter_t
static constexpr bool is_iterator = true;
static constexpr bool is_random_access_iterator = false;
static constexpr bool is_sorted_iterator = false;
+ static constexpr bool has_fast_len = false; // Should be checked in combination with is_random_access_iterator.
private:
/* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
@@ -393,7 +394,7 @@ struct hb_map_iter_t :
private:
Iter it;
- hb_reference_wrapper<Proj> f;
+ mutable hb_reference_wrapper<Proj> f;
};
template <typename Proj, hb_function_sortedness_t Sorted>
@@ -456,8 +457,8 @@ struct hb_filter_iter_t :
private:
Iter it;
- hb_reference_wrapper<Pred> p;
- hb_reference_wrapper<Proj> f;
+ mutable hb_reference_wrapper<Pred> p;
+ mutable hb_reference_wrapper<Proj> f;
};
template <typename Pred, typename Proj>
struct hb_filter_iter_factory_t
@@ -841,7 +842,7 @@ struct
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
auto operator () (Iterable&& it, unsigned count) const HB_AUTO_RETURN
- ( hb_zip (hb_range (count), it) | hb_map (hb_second) )
+ ( hb_zip (hb_range (count), it) | hb_map_retains_sorting (hb_second) )
/* Specialization arrays. */
diff --git a/thirdparty/harfbuzz/src/hb-kern.hh b/thirdparty/harfbuzz/src/hb-kern.hh
index 9ea945caed..9ac744c9dd 100644
--- a/thirdparty/harfbuzz/src/hb-kern.hh
+++ b/thirdparty/harfbuzz/src/hb-kern.hh
@@ -53,7 +53,7 @@ struct hb_kern_machine_t
return;
buffer->unsafe_to_concat ();
- OT::hb_ot_apply_context_t c (1, font, buffer);
+ OT::hb_ot_apply_context_t c (1, font, buffer, hb_blob_get_empty ());
c.set_lookup_mask (kern_mask);
c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
auto &skippy_iter = c.iter_input;
diff --git a/thirdparty/harfbuzz/src/hb-limits.hh b/thirdparty/harfbuzz/src/hb-limits.hh
index 0f60e9e210..c503b30652 100644
--- a/thirdparty/harfbuzz/src/hb-limits.hh
+++ b/thirdparty/harfbuzz/src/hb-limits.hh
@@ -89,6 +89,10 @@
#endif
+#ifndef HB_GLYF_VAR_COMPOSITE_MAX_AXES
+#define HB_GLYF_VAR_COMPOSITE_MAX_AXES 4096
+#endif
+
#ifndef HB_GLYF_MAX_POINTS
#define HB_GLYF_MAX_POINTS 20000
#endif
diff --git a/thirdparty/harfbuzz/src/hb-machinery.hh b/thirdparty/harfbuzz/src/hb-machinery.hh
index 1084725af2..cde1e99d6f 100644
--- a/thirdparty/harfbuzz/src/hb-machinery.hh
+++ b/thirdparty/harfbuzz/src/hb-machinery.hh
@@ -131,6 +131,10 @@ static inline Type& StructAfter(TObject &X)
unsigned int get_size () const { return (size - (array).min_size + (array).get_size ()); } \
DEFINE_SIZE_ARRAY(size, array)
+#define DEFINE_SIZE_MAX(size) \
+ DEFINE_INSTANCE_ASSERTION (sizeof (*this) <= (size)) \
+ static constexpr unsigned max_size = (size)
+
/*
@@ -180,6 +184,9 @@ struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData>
hb_lazy_loader_t<Returned,Subclass,Data,WheresData,Stored>
>::value Funcs;
+ hb_lazy_loader_t () = default;
+ hb_lazy_loader_t (const hb_lazy_loader_t &other) = delete;
+
void init0 () {} /* Init, when memory is already set to 0. No-op for us. */
void init () { instance.set_relaxed (nullptr); }
void fini () { do_destroy (instance.get_acquire ()); init (); }
@@ -278,7 +285,11 @@ struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData>
template <typename T, unsigned int WheresFace>
struct hb_face_lazy_loader_t : hb_lazy_loader_t<T,
hb_face_lazy_loader_t<T, WheresFace>,
- hb_face_t, WheresFace> {};
+ hb_face_t, WheresFace>
+{
+ // Hack; have them here for API parity with hb_table_lazy_loader_t
+ hb_blob_t *get_blob () { return this->get ()->get_blob (); }
+};
template <typename T, unsigned int WheresFace, bool core=false>
struct hb_table_lazy_loader_t : hb_lazy_loader_t<T,
@@ -288,7 +299,7 @@ struct hb_table_lazy_loader_t : hb_lazy_loader_t<T,
{
static hb_blob_t *create (hb_face_t *face)
{
- auto c = hb_sanitize_context_t ();
+ hb_sanitize_context_t c;
if (core)
c.set_num_glyphs (0); // So we don't recurse ad infinitum, or doesn't need num_glyphs
return c.reference_table<T> (face);
diff --git a/thirdparty/harfbuzz/src/hb-map.h b/thirdparty/harfbuzz/src/hb-map.h
index e928628fa7..0ae171714e 100644
--- a/thirdparty/harfbuzz/src/hb-map.h
+++ b/thirdparty/harfbuzz/src/hb-map.h
@@ -44,7 +44,7 @@ HB_BEGIN_DECLS
*
* Since: 1.7.7
*/
-#define HB_MAP_VALUE_INVALID ((hb_codepoint_t) -1)
+#define HB_MAP_VALUE_INVALID HB_CODEPOINT_INVALID
/**
* hb_map_t:
diff --git a/thirdparty/harfbuzz/src/hb-map.hh b/thirdparty/harfbuzz/src/hb-map.hh
index c685a9a3e1..e8abeec636 100644
--- a/thirdparty/harfbuzz/src/hb-map.hh
+++ b/thirdparty/harfbuzz/src/hb-map.hh
@@ -45,9 +45,9 @@ struct hb_hashmap_t
hb_hashmap_t () { init (); }
~hb_hashmap_t () { fini (); }
- hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (o.population); hb_copy (o, *this); }
+ hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { alloc (o.population); hb_copy (o, *this); }
hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); }
- hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); resize (o.population); hb_copy (o, *this); return *this; }
+ hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); alloc (o.population); hb_copy (o, *this); return *this; }
hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; }
hb_hashmap_t (std::initializer_list<hb_pair_t<K, V>> lst) : hb_hashmap_t ()
@@ -60,29 +60,28 @@ struct hb_hashmap_t
hb_hashmap_t (const Iterable &o) : hb_hashmap_t ()
{
auto iter = hb_iter (o);
- if (iter.is_random_access_iterator)
- resize (hb_len (iter));
+ if (iter.is_random_access_iterator || iter.has_fast_len)
+ alloc (hb_len (iter));
hb_copy (iter, *this);
}
struct item_t
{
K key;
- uint32_t hash : 30;
+ uint32_t is_real_ : 1;
uint32_t is_used_ : 1;
- uint32_t is_tombstone_ : 1;
+ uint32_t hash : 30;
V value;
item_t () : key (),
+ is_real_ (false), is_used_ (false),
hash (0),
- is_used_ (false), is_tombstone_ (false),
value () {}
bool is_used () const { return is_used_; }
void set_used (bool is_used) { is_used_ = is_used; }
- bool is_tombstone () const { return is_tombstone_; }
- void set_tombstone (bool is_tombstone) { is_tombstone_ = is_tombstone; }
- bool is_real () const { return is_used_ && !is_tombstone_; }
+ void set_real (bool is_real) { is_real_ = is_real; }
+ bool is_real () const { return is_real_; }
template <bool v = minus_one,
hb_enable_if (v == false)>
@@ -98,10 +97,15 @@ struct hb_hashmap_t
bool operator == (const K &o) const { return hb_deref (key) == hb_deref (o); }
bool operator == (const item_t &o) const { return *this == o.key; }
hb_pair_t<K, V> get_pair() const { return hb_pair_t<K, V> (key, value); }
- hb_pair_t<const K &, const V &> get_pair_ref() const { return hb_pair_t<const K &, const V &> (key, value); }
+ hb_pair_t<const K &, V &> get_pair_ref() { return hb_pair_t<const K &, V &> (key, value); }
uint32_t total_hash () const
{ return (hash * 31) + hb_hash (value); }
+
+ static constexpr bool is_trivial = std::is_trivially_constructible<K>::value &&
+ std::is_trivially_destructible<K>::value &&
+ std::is_trivially_constructible<V>::value &&
+ std::is_trivially_destructible<V>::value;
};
hb_object_header_t header;
@@ -110,6 +114,7 @@ struct hb_hashmap_t
unsigned int occupancy; /* Including tombstones. */
unsigned int mask;
unsigned int prime;
+ unsigned int max_chain_length;
item_t *items;
friend void swap (hb_hashmap_t& a, hb_hashmap_t& b)
@@ -123,6 +128,7 @@ struct hb_hashmap_t
hb_swap (a.occupancy, b.occupancy);
hb_swap (a.mask, b.mask);
hb_swap (a.prime, b.prime);
+ hb_swap (a.max_chain_length, b.max_chain_length);
hb_swap (a.items, b.items);
}
void init ()
@@ -133,16 +139,19 @@ struct hb_hashmap_t
population = occupancy = 0;
mask = 0;
prime = 0;
+ max_chain_length = 0;
items = nullptr;
}
void fini ()
{
hb_object_fini (this);
- if (likely (items)) {
+ if (likely (items))
+ {
unsigned size = mask + 1;
- for (unsigned i = 0; i < size; i++)
- items[i].~item_t ();
+ if (!item_t::is_trivial)
+ for (unsigned i = 0; i < size; i++)
+ items[i].~item_t ();
hb_free (items);
items = nullptr;
}
@@ -157,7 +166,7 @@ struct hb_hashmap_t
bool in_error () const { return !successful; }
- bool resize (unsigned new_population = 0)
+ bool alloc (unsigned new_population = 0)
{
if (unlikely (!successful)) return false;
@@ -171,8 +180,11 @@ struct hb_hashmap_t
successful = false;
return false;
}
- for (auto &_ : hb_iter (new_items, new_size))
- new (&_) item_t ();
+ if (!item_t::is_trivial)
+ for (auto &_ : hb_iter (new_items, new_size))
+ new (&_) item_t ();
+ else
+ hb_memset (new_items, 0, (size_t) new_size * sizeof (item_t));
unsigned int old_size = size ();
item_t *old_items = items;
@@ -181,6 +193,7 @@ struct hb_hashmap_t
population = occupancy = 0;
mask = new_size - 1;
prime = prime_for (power);
+ max_chain_length = power * 2;
items = new_items;
/* Insert back old items. */
@@ -192,7 +205,8 @@ struct hb_hashmap_t
old_items[i].hash,
std::move (old_items[i].value));
}
- old_items[i].~item_t ();
+ if (!item_t::is_trivial)
+ old_items[i].~item_t ();
}
hb_free (old_items);
@@ -201,72 +215,124 @@ struct hb_hashmap_t
}
template <typename KK, typename VV>
- bool set_with_hash (KK&& key, uint32_t hash, VV&& value, bool is_delete=false)
+ bool set_with_hash (KK&& key, uint32_t hash, VV&& value, bool overwrite = true)
{
if (unlikely (!successful)) return false;
- if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false;
- item_t &item = item_for_hash (key, hash);
+ if (unlikely ((occupancy + occupancy / 2) >= mask && !alloc ())) return false;
+
+ hash &= 0x3FFFFFFF; // We only store lower 30bit of hash
+ unsigned int tombstone = (unsigned int) -1;
+ unsigned int i = hash % prime;
+ unsigned length = 0;
+ unsigned step = 0;
+ while (items[i].is_used ())
+ {
+ if ((std::is_integral<K>::value || items[i].hash == hash) &&
+ items[i] == key)
+ {
+ if (!overwrite)
+ return false;
+ else
+ break;
+ }
+ if (!items[i].is_real () && tombstone == (unsigned) -1)
+ tombstone = i;
+ i = (i + ++step) & mask;
+ length++;
+ }
- if (is_delete && !(item == key))
- return true; /* Trying to delete non-existent key. */
+ item_t &item = items[tombstone == (unsigned) -1 ? i : tombstone];
if (item.is_used ())
{
occupancy--;
- if (!item.is_tombstone ())
- population--;
+ population -= item.is_real ();
}
item.key = std::forward<KK> (key);
item.value = std::forward<VV> (value);
item.hash = hash;
item.set_used (true);
- item.set_tombstone (is_delete);
+ item.set_real (true);
occupancy++;
- if (!is_delete)
- population++;
+ population++;
+
+ if (unlikely (length > max_chain_length) && occupancy * 8 > mask)
+ alloc (mask - 8); // This ensures we jump to next larger size
return true;
}
template <typename VV>
- bool set (const K &key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward<VV> (value)); }
+ bool set (const K &key, VV&& value, bool overwrite = true) { return set_with_hash (key, hb_hash (key), std::forward<VV> (value), overwrite); }
template <typename VV>
- bool set (K &&key, VV&& value) { return set_with_hash (std::move (key), hb_hash (key), std::forward<VV> (value)); }
+ bool set (K &&key, VV&& value, bool overwrite = true)
+ {
+ uint32_t hash = hb_hash (key);
+ return set_with_hash (std::move (key), hash, std::forward<VV> (value), overwrite);
+ }
const V& get_with_hash (const K &key, uint32_t hash) const
{
- if (unlikely (!items)) return item_t::default_value ();
- auto &item = item_for_hash (key, hash);
- return item.is_real () && item == key ? item.value : item_t::default_value ();
+ if (!items) return item_t::default_value ();
+ auto *item = fetch_item (key, hb_hash (key));
+ if (item)
+ return item->value;
+ return item_t::default_value ();
}
const V& get (const K &key) const
{
- if (unlikely (!items)) return item_t::default_value ();
+ if (!items) return item_t::default_value ();
return get_with_hash (key, hb_hash (key));
}
- void del (const K &key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); }
+ void del (const K &key)
+ {
+ if (!items) return;
+ auto *item = fetch_item (key, hb_hash (key));
+ if (item)
+ {
+ item->set_real (false);
+ population--;
+ }
+ }
/* Has interface. */
const V& operator [] (K k) const { return get (k); }
template <typename VV=V>
- bool has (K key, VV **vp = nullptr) const
+ bool has (const K &key, VV **vp = nullptr) const
{
- if (unlikely (!items))
- return false;
- auto &item = item_for_hash (key, hb_hash (key));
- if (item.is_real () && item == key)
+ if (!items) return false;
+ auto *item = fetch_item (key, hb_hash (key));
+ if (item)
{
- if (vp) *vp = std::addressof (item.value);
+ if (vp) *vp = std::addressof (item->value);
return true;
}
- else
- return false;
+ return false;
+ }
+ item_t *fetch_item (const K &key, uint32_t hash) const
+ {
+ hash &= 0x3FFFFFFF; // We only store lower 30bit of hash
+ unsigned int i = hash % prime;
+ unsigned step = 0;
+ while (items[i].is_used ())
+ {
+ if ((std::is_integral<K>::value || items[i].hash == hash) &&
+ items[i] == key)
+ {
+ if (items[i].is_real ())
+ return &items[i];
+ else
+ return nullptr;
+ }
+ i = (i + ++step) & mask;
+ }
+ return nullptr;
}
/* Projection. */
- V operator () (K k) const { return get (k); }
+ const V& operator () (K k) const { return get (k); }
unsigned size () const { return mask ? mask + 1 : 0; }
@@ -393,24 +459,6 @@ struct hb_hashmap_t
hb_hashmap_t& operator << (const hb_pair_t<K&&, V&&>& v)
{ set (std::move (v.first), std::move (v.second)); return *this; }
- item_t& item_for_hash (const K &key, uint32_t hash) const
- {
- hash &= 0x3FFFFFFF; // We only store lower 30bit of hash
- unsigned int i = hash % prime;
- unsigned int step = 0;
- unsigned int tombstone = (unsigned) -1;
- while (items[i].is_used ())
- {
- if ((hb_is_same (K, hb_codepoint_t) || items[i].hash == hash) &&
- items[i] == key)
- return items[i];
- if (tombstone == (unsigned) -1 && items[i].is_tombstone ())
- tombstone = i;
- i = (i + ++step) & mask;
- }
- return items[tombstone == (unsigned) -1 ? i : tombstone];
- }
-
static unsigned int prime_for (unsigned int shift)
{
/* Following comment and table copied from glib. */
@@ -481,7 +529,7 @@ struct hb_map_t : hb_hashmap_t<hb_codepoint_t,
hb_map_t (hb_map_t &&o) : hashmap (std::move ((hashmap &) o)) {}
hb_map_t& operator= (const hb_map_t&) = default;
hb_map_t& operator= (hb_map_t&&) = default;
- hb_map_t (std::initializer_list<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> lst) : hashmap (lst) {}
+ hb_map_t (std::initializer_list<hb_codepoint_pair_t> lst) : hashmap (lst) {}
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
hb_map_t (const Iterable &o) : hashmap (o) {}
diff --git a/thirdparty/harfbuzz/src/hb-meta.hh b/thirdparty/harfbuzz/src/hb-meta.hh
index 31aa7fa6f1..52ff4a8412 100644
--- a/thirdparty/harfbuzz/src/hb-meta.hh
+++ b/thirdparty/harfbuzz/src/hb-meta.hh
@@ -153,8 +153,8 @@ struct hb_reference_wrapper
hb_reference_wrapper (T v) : v (v) {}
bool operator == (const hb_reference_wrapper& o) const { return v == o.v; }
bool operator != (const hb_reference_wrapper& o) const { return v != o.v; }
- operator T () const { return v; }
- T get () const { return v; }
+ operator T& () { return v; }
+ T& get () { return v; }
T v;
};
template <typename T>
@@ -163,8 +163,8 @@ struct hb_reference_wrapper<T&>
hb_reference_wrapper (T& v) : v (std::addressof (v)) {}
bool operator == (const hb_reference_wrapper& o) const { return v == o.v; }
bool operator != (const hb_reference_wrapper& o) const { return v != o.v; }
- operator T& () const { return *v; }
- T& get () const { return *v; }
+ operator T& () { return *v; }
+ T& get () { return *v; }
T* v;
};
diff --git a/thirdparty/harfbuzz/src/hb-multimap.hh b/thirdparty/harfbuzz/src/hb-multimap.hh
index b4a8cc62a3..0184279c12 100644
--- a/thirdparty/harfbuzz/src/hb-multimap.hh
+++ b/thirdparty/harfbuzz/src/hb-multimap.hh
@@ -38,10 +38,10 @@ struct hb_multimap_t
{
void add (hb_codepoint_t k, hb_codepoint_t v)
{
- hb_codepoint_t *i;
- if (multiples_indices.has (k, &i))
+ hb_vector_t<hb_codepoint_t> *m;
+ if (multiples.has (k, &m))
{
- multiples_values[*i].push (v);
+ m->push (v);
return;
}
@@ -51,12 +51,7 @@ struct hb_multimap_t
hb_codepoint_t old = *old_v;
singulars.del (k);
- multiples_indices.set (k, multiples_values.length);
- auto *vec = multiples_values.push ();
-
- vec->push (old);
- vec->push (v);
-
+ multiples.set (k, hb_vector_t<hb_codepoint_t> {old, v});
return;
}
@@ -69,22 +64,31 @@ struct hb_multimap_t
if (singulars.has (k, &v))
return hb_array (v, 1);
- hb_codepoint_t *i;
- if (multiples_indices.has (k, &i))
- return multiples_values[*i].as_array ();
+ hb_vector_t<hb_codepoint_t> *m;
+ if (multiples.has (k, &m))
+ return m->as_array ();
return hb_array_t<const hb_codepoint_t> ();
}
bool in_error () const
{
- return singulars.in_error () || multiples_indices.in_error () || multiples_values.in_error ();
+ if (singulars.in_error () || multiples.in_error ())
+ return true;
+ for (const auto &m : multiples.values_ref ())
+ if (m.in_error ())
+ return true;
+ return false;
+ }
+
+ void alloc (unsigned size)
+ {
+ singulars.alloc (size);
}
protected:
hb_map_t singulars;
- hb_map_t multiples_indices;
- hb_vector_t<hb_vector_t<hb_codepoint_t>> multiples_values;
+ hb_hashmap_t<hb_codepoint_t, hb_vector_t<hb_codepoint_t>> multiples;
};
diff --git a/thirdparty/harfbuzz/src/hb-null.hh b/thirdparty/harfbuzz/src/hb-null.hh
index 3da2d75ef5..2982516283 100644
--- a/thirdparty/harfbuzz/src/hb-null.hh
+++ b/thirdparty/harfbuzz/src/hb-null.hh
@@ -49,6 +49,15 @@ using hb_has_min_size = _hb_has_min_size<T, void>;
#define hb_has_min_size(T) hb_has_min_size<T>::value
template <typename T, typename>
+struct _hb_has_max_size : hb_false_type {};
+template <typename T>
+struct _hb_has_max_size<T, hb_void_t<decltype (T::max_size)>>
+ : hb_true_type {};
+template <typename T>
+using hb_has_max_size = _hb_has_max_size<T, void>;
+#define hb_has_max_size(T) hb_has_max_size<T>::value
+
+template <typename T, typename>
struct _hb_has_null_size : hb_false_type {};
template <typename T>
struct _hb_has_null_size<T, hb_void_t<decltype (T::null_size)>>
@@ -176,7 +185,7 @@ template <typename Type>
static inline Type& Crap () {
static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
Type *obj = reinterpret_cast<Type *> (_hb_CrapPool);
- memcpy (obj, &Null (Type), sizeof (*obj));
+ memcpy (obj, std::addressof (Null (Type)), sizeof (*obj));
return *obj;
}
template <typename QType>
@@ -211,11 +220,11 @@ struct hb_nonnull_ptr_t
T * operator = (T *v_) { return v = v_; }
T * operator -> () const { return get (); }
T & operator * () const { return *get (); }
- T ** operator & () const { return &v; }
+ T ** operator & () const { return std::addressof (v); }
/* Only auto-cast to const types. */
template <typename C> operator const C * () const { return get (); }
operator const char * () const { return (const char *) get (); }
- T * get () const { return v ? v : const_cast<T *> (&Null (T)); }
+ T * get () const { return v ? v : const_cast<T *> (std::addressof (Null (T))); }
T * get_raw () const { return v; }
private:
diff --git a/thirdparty/harfbuzz/src/hb-open-file.hh b/thirdparty/harfbuzz/src/hb-open-file.hh
index 13570a46e0..04f144a72c 100644
--- a/thirdparty/harfbuzz/src/hb-open-file.hh
+++ b/thirdparty/harfbuzz/src/hb-open-file.hh
@@ -131,7 +131,7 @@ typedef struct OpenTypeOffsetTable
sfnt_version = sfnt_tag;
/* Take space for numTables, searchRange, entrySelector, RangeShift
* and the TableRecords themselves. */
- unsigned num_items = it.len ();
+ unsigned num_items = hb_len (it);
if (unlikely (!tables.serialize (c, num_items))) return_trace (false);
const char *dir_end = (const char *) c->head;
@@ -145,7 +145,7 @@ typedef struct OpenTypeOffsetTable
unsigned len = blob->length;
/* Allocate room for the table and copy it. */
- char *start = (char *) c->allocate_size<void> (len);
+ char *start = (char *) c->allocate_size<void> (len, false);
if (unlikely (!start)) return false;
TableRecord &rec = tables.arrayZ[i];
diff --git a/thirdparty/harfbuzz/src/hb-open-type.hh b/thirdparty/harfbuzz/src/hb-open-type.hh
index 4c9bfebcec..6d464a3535 100644
--- a/thirdparty/harfbuzz/src/hb-open-type.hh
+++ b/thirdparty/harfbuzz/src/hb-open-type.hh
@@ -312,6 +312,8 @@ struct _hb_has_null<Type, true>
template <typename Type, typename OffsetType, bool has_null=true>
struct OffsetTo : Offset<OffsetType, has_null>
{
+ using target_t = Type;
+
// Make sure Type is not unbounded; works only for types that are fully defined at OffsetTo time.
static_assert (has_null == false ||
(hb_has_null_size (Type) || !hb_has_min_size (Type)), "");
@@ -416,12 +418,15 @@ struct OffsetTo : Offset<OffsetType, has_null>
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this))) return_trace (false);
- if (unlikely (this->is_null ())) return_trace (true);
+ //if (unlikely (this->is_null ())) return_trace (true);
if (unlikely ((const char *) base + (unsigned) *this < (const char *) base)) return_trace (false);
return_trace (true);
}
template <typename ...Ts>
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const
{
TRACE_SANITIZE (this);
@@ -462,24 +467,16 @@ struct UnsizedArrayOf
HB_DELETE_CREATE_COPY_ASSIGN (UnsizedArrayOf);
- const Type& operator [] (int i_) const
+ const Type& operator [] (unsigned int i) const
{
- unsigned int i = (unsigned int) i_;
- const Type *p = &arrayZ[i];
- if (unlikely ((const void *) p < (const void *) arrayZ)) return Null (Type); /* Overflowed. */
- _hb_compiler_memory_r_barrier ();
- return *p;
+ return arrayZ[i];
}
- Type& operator [] (int i_)
+ Type& operator [] (unsigned int i)
{
- unsigned int i = (unsigned int) i_;
- Type *p = &arrayZ[i];
- if (unlikely ((const void *) p < (const void *) arrayZ)) return Crap (Type); /* Overflowed. */
- _hb_compiler_memory_r_barrier ();
- return *p;
+ return arrayZ[i];
}
- unsigned int get_size (unsigned int len) const
+ static unsigned int get_size (unsigned int len)
{ return len * Type::static_size; }
template <typename T> operator T * () { return arrayZ; }
@@ -533,6 +530,7 @@ struct UnsizedArrayOf
}
template <typename ...Ts>
+ HB_ALWAYS_INLINE
bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const
{
TRACE_SANITIZE (this);
@@ -720,7 +718,32 @@ struct ArrayOf
return_trace (out);
}
+ /* Special-case ArrayOf Offset16To structs with a maximum size. */
+ template <typename T = Type,
+ typename Base = void,
+ hb_enable_if (hb_has_max_size (typename T::target_t) &&
+ sizeof (T) == 2)>
+ HB_ALWAYS_INLINE
+ bool sanitize (hb_sanitize_context_t *c, const Base *base) const
+ {
+ TRACE_SANITIZE (this);
+
+ if (unlikely (!sanitize_shallow (c))) return_trace (false);
+
+ unsigned max_len = 65536 + Type::target_t::max_size;
+
+ if (unlikely (c->check_range_fast (base, max_len)))
+ return_trace (true);
+
+ unsigned int count = len;
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!c->dispatch (arrayZ[i], base)))
+ return_trace (false);
+ return_trace (true);
+ }
+
template <typename ...Ts>
+ HB_ALWAYS_INLINE
bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
{
TRACE_SANITIZE (this);
@@ -736,7 +759,7 @@ struct ArrayOf
bool sanitize_shallow (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- return_trace (len.sanitize (c) && c->check_array (arrayZ, len));
+ return_trace (len.sanitize (c) && c->check_array_sized (arrayZ, len, sizeof (LenType)));
}
public:
@@ -797,7 +820,7 @@ template <typename Type>
using List16OfOffset16To = List16OfOffsetTo<Type, HBUINT16>;
/* An array starting at second element. */
-template <typename Type, typename LenType=HBUINT16>
+template <typename Type, typename LenType>
struct HeadlessArrayOf
{
static constexpr unsigned item_size = Type::static_size;
@@ -861,6 +884,7 @@ struct HeadlessArrayOf
}
template <typename ...Ts>
+ HB_ALWAYS_INLINE
bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
{
TRACE_SANITIZE (this);
@@ -878,7 +902,7 @@ struct HeadlessArrayOf
{
TRACE_SANITIZE (this);
return_trace (lenP1.sanitize (c) &&
- (!lenP1 || c->check_array (arrayZ, lenP1 - 1)));
+ (!lenP1 || c->check_array_sized (arrayZ, lenP1 - 1, sizeof (LenType))));
}
public:
@@ -887,6 +911,7 @@ struct HeadlessArrayOf
public:
DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
};
+template <typename Type> using HeadlessArray16Of = HeadlessArrayOf<Type, HBUINT16>;
/* An array storing length-1. */
template <typename Type, typename LenType=HBUINT16>
@@ -912,6 +937,7 @@ struct ArrayOfM1
{ return lenM1.static_size + (lenM1 + 1) * Type::static_size; }
template <typename ...Ts>
+ HB_ALWAYS_INLINE
bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
{
TRACE_SANITIZE (this);
@@ -929,7 +955,7 @@ struct ArrayOfM1
{
TRACE_SANITIZE (this);
return_trace (lenM1.sanitize (c) &&
- (c->check_array (arrayZ, lenM1 + 1)));
+ (c->check_array_sized (arrayZ, lenM1 + 1, sizeof (LenType))));
}
public:
@@ -1096,6 +1122,7 @@ struct VarSizedBinSearchArrayOf
{ return header.static_size + header.nUnits * header.unitSize; }
template <typename ...Ts>
+ HB_ALWAYS_INLINE
bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
{
TRACE_SANITIZE (this);
diff --git a/thirdparty/harfbuzz/src/hb-ot-cff-common.hh b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh
index f22824fc69..a2f90c8f5e 100644
--- a/thirdparty/harfbuzz/src/hb-ot-cff-common.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh
@@ -48,12 +48,24 @@ static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offs
struct code_pair_t
{
- hb_codepoint_t code;
+ unsigned code;
hb_codepoint_t glyph;
};
+
using str_buff_t = hb_vector_t<unsigned char>;
using str_buff_vec_t = hb_vector_t<str_buff_t>;
+using glyph_to_sid_map_t = hb_vector_t<code_pair_t>;
+
+struct length_f_t
+{
+ template <typename Iterable,
+ hb_requires (hb_is_iterable (Iterable))>
+ unsigned operator () (const Iterable &_) const { return hb_len (hb_iter (_)); }
+
+ unsigned operator () (unsigned _) const { return _; }
+}
+HB_FUNCOBJ (length_f);
/* CFF INDEX */
template <typename COUNT>
@@ -62,42 +74,52 @@ struct CFFIndex
unsigned int offset_array_size () const
{ return offSize * (count + 1); }
- CFFIndex *copy (hb_serialize_context_t *c) const
- {
- TRACE_SERIALIZE (this);
- unsigned int size = get_size ();
- CFFIndex *out = c->allocate_size<CFFIndex> (size, false);
- if (likely (out))
- hb_memcpy (out, this, size);
- return_trace (out);
- }
-
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
bool serialize (hb_serialize_context_t *c,
- const Iterable &iterable)
+ const Iterable &iterable,
+ const unsigned *p_data_size = nullptr)
{
TRACE_SERIALIZE (this);
+ unsigned data_size;
+ if (p_data_size)
+ data_size = *p_data_size;
+ else
+ total_size (iterable, &data_size);
+
auto it = hb_iter (iterable);
- serialize_header(c, + it | hb_map (hb_iter) | hb_map (hb_len));
+ if (unlikely (!serialize_header (c, +it, data_size))) return_trace (false);
+ unsigned char *ret = c->allocate_size<unsigned char> (data_size, false);
+ if (unlikely (!ret)) return_trace (false);
for (const auto &_ : +it)
- hb_iter (_).copy (c);
+ {
+ unsigned len = _.length;
+ if (len <= 1)
+ {
+ if (!len)
+ continue;
+ *ret++ = *_.arrayZ;
+ continue;
+ }
+ hb_memcpy (ret, _.arrayZ, len);
+ ret += len;
+ }
return_trace (true);
}
template <typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
bool serialize_header (hb_serialize_context_t *c,
- Iterator it)
+ Iterator it,
+ unsigned data_size)
{
TRACE_SERIALIZE (this);
- unsigned total = + it | hb_reduce (hb_add, 0);
- unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8;
+ unsigned off_size = (hb_bit_storage (data_size + 1) + 7) / 8;
/* serialize CFFIndex header */
if (unlikely (!c->extend_min (this))) return_trace (false);
- this->count = it.len ();
+ this->count = hb_len (it);
if (!this->count) return_trace (true);
if (unlikely (!c->extend (this->offSize))) return_trace (false);
this->offSize = off_size;
@@ -106,25 +128,88 @@ struct CFFIndex
/* serialize indices */
unsigned int offset = 1;
- unsigned int i = 0;
- for (unsigned _ : +it)
+ if (HB_OPTIMIZE_SIZE_VAL)
{
- set_offset_at (i++, offset);
- offset += _;
+ unsigned int i = 0;
+ for (const auto &_ : +it)
+ {
+ set_offset_at (i++, offset);
+ offset += length_f (_);
+ }
+ set_offset_at (i, offset);
}
- set_offset_at (i, offset);
-
+ else
+ switch (off_size)
+ {
+ case 1:
+ {
+ HBUINT8 *p = (HBUINT8 *) offsets;
+ for (const auto &_ : +it)
+ {
+ *p++ = offset;
+ offset += length_f (_);
+ }
+ *p = offset;
+ }
+ break;
+ case 2:
+ {
+ HBUINT16 *p = (HBUINT16 *) offsets;
+ for (const auto &_ : +it)
+ {
+ *p++ = offset;
+ offset += length_f (_);
+ }
+ *p = offset;
+ }
+ break;
+ case 3:
+ {
+ HBUINT24 *p = (HBUINT24 *) offsets;
+ for (const auto &_ : +it)
+ {
+ *p++ = offset;
+ offset += length_f (_);
+ }
+ *p = offset;
+ }
+ break;
+ case 4:
+ {
+ HBUINT32 *p = (HBUINT32 *) offsets;
+ for (const auto &_ : +it)
+ {
+ *p++ = offset;
+ offset += length_f (_);
+ }
+ *p = offset;
+ }
+ break;
+ default:
+ break;
+ }
+
+ assert (offset == data_size + 1);
return_trace (true);
}
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
- static unsigned total_size (const Iterable &iterable)
+ static unsigned total_size (const Iterable &iterable, unsigned *data_size = nullptr)
{
- auto it = + hb_iter (iterable) | hb_map (hb_iter) | hb_map (hb_len);
- if (!it) return 0;
+ auto it = + hb_iter (iterable);
+ if (!it)
+ {
+ if (data_size) *data_size = 0;
+ return min_size;
+ }
+
+ unsigned total = 0;
+ for (const auto &_ : +it)
+ total += length_f (_);
+
+ if (data_size) *data_size = total;
- unsigned total = + it | hb_reduce (hb_add, 0);
unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8;
return min_size + HBUINT8::static_size + (hb_len (it) + 1) * off_size + total;
@@ -133,13 +218,16 @@ struct CFFIndex
void set_offset_at (unsigned int index, unsigned int offset)
{
assert (index <= count);
- HBUINT8 *p = offsets + offSize * index + offSize;
+
unsigned int size = offSize;
- for (; size; size--)
+ const HBUINT8 *p = offsets;
+ switch (size)
{
- --p;
- *p = offset & 0xFF;
- offset >>= 8;
+ case 1: ((HBUINT8 *) p)[index] = offset; break;
+ case 2: ((HBUINT16 *) p)[index] = offset; break;
+ case 3: ((HBUINT24 *) p)[index] = offset; break;
+ case 4: ((HBUINT32 *) p)[index] = offset; break;
+ default: return;
}
}
@@ -149,37 +237,30 @@ struct CFFIndex
assert (index <= count);
unsigned int size = offSize;
- const HBUINT8 *p = offsets + size * index;
+ const HBUINT8 *p = offsets;
switch (size)
{
- case 1: return * (HBUINT8 *) p;
- case 2: return * (HBUINT16 *) p;
- case 3: return * (HBUINT24 *) p;
- case 4: return * (HBUINT32 *) p;
+ case 1: return ((HBUINT8 *) p)[index];
+ case 2: return ((HBUINT16 *) p)[index];
+ case 3: return ((HBUINT24 *) p)[index];
+ case 4: return ((HBUINT32 *) p)[index];
default: return 0;
}
}
- unsigned int length_at (unsigned int index) const
- {
- unsigned offset0 = offset_at (index);
- unsigned offset1 = offset_at (index + 1);
- if (unlikely (offset1 < offset0 || offset1 > offset_at (count)))
- return 0;
- return offset1 - offset0;
- }
-
const unsigned char *data_base () const
- { return (const unsigned char *) this + min_size + offSize.static_size + offset_array_size (); }
+ { return (const unsigned char *) this + min_size + offSize.static_size - 1 + offset_array_size (); }
public:
hb_ubytes_t operator [] (unsigned int index) const
{
if (unlikely (index >= count)) return hb_ubytes_t ();
_hb_compiler_memory_r_barrier ();
- unsigned length = length_at (index);
- if (unlikely (!length)) return hb_ubytes_t ();
- return hb_ubytes_t (data_base () + offset_at (index) - 1, length);
+ unsigned offset0 = offset_at (index);
+ unsigned offset1 = offset_at (index + 1);
+ if (unlikely (offset1 < offset0 || offset1 > offset_at (count)))
+ return hb_ubytes_t ();
+ return hb_ubytes_t (data_base () + offset0, offset1 - offset0);
}
unsigned int get_size () const
@@ -197,7 +278,7 @@ struct CFFIndex
(count < count + 1u &&
c->check_struct (&offSize) && offSize >= 1 && offSize <= 4 &&
c->check_array (offsets, offSize, count + 1u) &&
- c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count) - 1)))));
+ c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count))))));
}
public:
@@ -211,47 +292,6 @@ struct CFFIndex
DEFINE_SIZE_MIN (COUNT::static_size);
};
-template <typename COUNT, typename TYPE>
-struct CFFIndexOf : CFFIndex<COUNT>
-{
- template <typename DATA, typename PARAM1, typename PARAM2>
- bool serialize (hb_serialize_context_t *c,
- unsigned int offSize_,
- const DATA *dataArray,
- unsigned int dataArrayLen,
- const hb_vector_t<unsigned int> &dataSizeArray,
- const PARAM1 &param1,
- const PARAM2 &param2)
- {
- TRACE_SERIALIZE (this);
- /* serialize CFFIndex header */
- if (unlikely (!c->extend_min (this))) return_trace (false);
- this->count = dataArrayLen;
- this->offSize = offSize_;
- if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1), false)))
- return_trace (false);
-
- /* serialize indices */
- unsigned int offset = 1;
- unsigned int i = 0;
- for (; i < dataArrayLen; i++)
- {
- this->set_offset_at (i, offset);
- offset += dataSizeArray[i];
- }
- this->set_offset_at (i, offset);
-
- /* serialize data */
- for (unsigned int i = 0; i < dataArrayLen; i++)
- {
- TYPE *dest = c->start_embed<TYPE> ();
- if (unlikely (!dest || !dest->serialize (c, dataArray[i], param1, param2)))
- return_trace (false);
- }
- return_trace (true);
- }
-};
-
/* Top Dict, Font Dict, Private Dict */
struct Dict : UnsizedByteStr
{
@@ -327,7 +367,7 @@ struct table_info_t
};
template <typename COUNT>
-struct FDArray : CFFIndexOf<COUNT, FontDict>
+struct FDArray : CFFIndex<COUNT>
{
template <typename DICTVAL, typename INFO, typename Iterator, typename OP_SERIALIZER>
bool serialize (hb_serialize_context_t *c,
@@ -338,7 +378,11 @@ struct FDArray : CFFIndexOf<COUNT, FontDict>
/* serialize INDEX data */
hb_vector_t<unsigned> sizes;
+ if (it.is_random_access_iterator)
+ sizes.alloc (hb_len (it));
+
c->push ();
+ char *data_base = c->head;
+ it
| hb_map ([&] (const hb_pair_t<const DICTVAL&, const INFO&> &_)
{
@@ -348,10 +392,16 @@ struct FDArray : CFFIndexOf<COUNT, FontDict>
})
| hb_sink (sizes)
;
+ unsigned data_size = c->head - data_base;
c->pop_pack (false);
+ if (unlikely (sizes.in_error ())) return_trace (false);
+
+ /* It just happens that the above is packed right after the header below.
+ * Such a hack. */
+
/* serialize INDEX header */
- return_trace (CFFIndex<COUNT>::serialize_header (c, hb_iter (sizes)));
+ return_trace (CFFIndex<COUNT>::serialize_header (c, hb_iter (sizes), data_size));
}
};
@@ -368,8 +418,11 @@ struct FDSelect0 {
return_trace (true);
}
- hb_codepoint_t get_fd (hb_codepoint_t glyph) const
- { return (hb_codepoint_t) fds[glyph]; }
+ unsigned get_fd (hb_codepoint_t glyph) const
+ { return fds[glyph]; }
+
+ hb_pair_t<unsigned, hb_codepoint_t> get_fd_range (hb_codepoint_t glyph) const
+ { return {fds[glyph], glyph + 1}; }
unsigned int get_size (unsigned int num_glyphs) const
{ return HBUINT8::static_size * num_glyphs; }
@@ -427,12 +480,20 @@ struct FDSelect3_4
return +1;
}
- hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ unsigned get_fd (hb_codepoint_t glyph) const
{
auto *range = hb_bsearch (glyph, &ranges[0], nRanges () - 1, sizeof (ranges[0]), _cmp_range);
return range ? range->fd : ranges[nRanges () - 1].fd;
}
+ hb_pair_t<unsigned, hb_codepoint_t> get_fd_range (hb_codepoint_t glyph) const
+ {
+ auto *range = hb_bsearch (glyph, &ranges[0], nRanges () - 1, sizeof (ranges[0]), _cmp_range);
+ unsigned fd = range ? range->fd : ranges[nRanges () - 1].fd;
+ hb_codepoint_t end = range ? range[1].first : ranges[nRanges () - 1].first;
+ return {fd, end};
+ }
+
GID_TYPE &nRanges () { return ranges.len; }
GID_TYPE nRanges () const { return ranges.len; }
GID_TYPE &sentinel () { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
@@ -469,7 +530,7 @@ struct FDSelect
}
}
- hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ unsigned get_fd (hb_codepoint_t glyph) const
{
if (this == &Null (FDSelect)) return 0;
@@ -480,6 +541,18 @@ struct FDSelect
default:return 0;
}
}
+ /* Returns pair of fd and one after last glyph in range. */
+ hb_pair_t<unsigned, hb_codepoint_t> get_fd_range (hb_codepoint_t glyph) const
+ {
+ if (this == &Null (FDSelect)) return {0, 1};
+
+ switch (format)
+ {
+ case 0: return u.format0.get_fd_range (glyph);
+ case 3: return u.format3.get_fd_range (glyph);
+ default:return {0, 1};
+ }
+ }
bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
{
diff --git a/thirdparty/harfbuzz/src/hb-ot-cff1-table.cc b/thirdparty/harfbuzz/src/hb-ot-cff1-table.cc
index 5040c74623..66df28aae1 100644
--- a/thirdparty/harfbuzz/src/hb-ot-cff1-table.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-cff1-table.cc
@@ -574,11 +574,11 @@ bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, h
struct get_seac_param_t
{
- get_seac_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff) {}
+ get_seac_param_t (const OT::cff1::accelerator_subset_t *_cff) : cff (_cff) {}
bool has_seac () const { return base && accent; }
- const OT::cff1::accelerator_t *cff;
+ const OT::cff1::accelerator_subset_t *cff;
hb_codepoint_t base = 0;
hb_codepoint_t accent = 0;
};
@@ -596,7 +596,7 @@ struct cff1_cs_opset_seac_t : cff1_cs_opset_t<cff1_cs_opset_seac_t, get_seac_par
}
};
-bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const
+bool OT::cff1::accelerator_subset_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const
{
if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
diff --git a/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh
index 4d0a965eee..3d658ac626 100644
--- a/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh
@@ -28,7 +28,7 @@
#define HB_OT_CFF1_TABLE_HH
#include "hb-ot-cff-common.hh"
-#include "hb-subset-cff1.hh"
+#include "hb-subset-cff-common.hh"
#include "hb-draw.hh"
#include "hb-paint.hh"
@@ -52,7 +52,6 @@ enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 };
enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 };
typedef CFFIndex<HBUINT16> CFF1Index;
-template <typename Type> struct CFF1IndexOf : CFFIndexOf<HBUINT16, Type> {};
typedef CFFIndex<HBUINT16> CFF1Index;
typedef CFF1Index CFF1CharStrings;
@@ -110,6 +109,7 @@ struct Encoding1 {
hb_codepoint_t get_code (hb_codepoint_t glyph) const
{
+ /* TODO: Add cache like get_sid. */
assert (glyph > 0);
glyph--;
for (unsigned int i = 0; i < nRanges (); i++)
@@ -173,11 +173,7 @@ struct Encoding
bool serialize (hb_serialize_context_t *c, const Encoding &src)
{
TRACE_SERIALIZE (this);
- unsigned int size = src.get_size ();
- Encoding *dest = c->allocate_size<Encoding> (size);
- if (unlikely (!dest)) return_trace (false);
- hb_memcpy (dest, &src, size);
- return_trace (true);
+ return_trace (c->embed (src));
}
/* serialize a subset Encoding */
@@ -312,26 +308,29 @@ struct Encoding
};
/* Charset */
-struct Charset0 {
- bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
+struct Charset0
+{
+ bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs, unsigned *num_charset_entries) const
{
TRACE_SANITIZE (this);
- return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c));
+ if (num_charset_entries) *num_charset_entries = num_glyphs;
+ return_trace (sids.sanitize (c, num_glyphs - 1));
}
hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const
{
if (unlikely (glyph >= num_glyphs)) return 0;
- if (glyph == 0)
+ if (unlikely (glyph == 0))
return 0;
else
return sids[glyph - 1];
}
- void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+ void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const
{
+ mapping->resize (num_glyphs, false);
for (hb_codepoint_t gid = 1; gid < num_glyphs; gid++)
- mapping->set (gid, sids[gid - 1]);
+ mapping->arrayZ[gid] = {sids[gid - 1], gid};
}
hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
@@ -347,13 +346,13 @@ struct Charset0 {
return 0;
}
- unsigned int get_size (unsigned int num_glyphs) const
+ static unsigned int get_size (unsigned int num_glyphs)
{
assert (num_glyphs > 0);
- return HBUINT16::static_size * (num_glyphs - 1);
+ return UnsizedArrayOf<HBUINT16>::get_size (num_glyphs - 1);
}
- HBUINT16 sids[HB_VAR_ARRAY];
+ UnsizedArrayOf<HBUINT16> sids;
DEFINE_SIZE_ARRAY(0, sids);
};
@@ -374,38 +373,62 @@ struct Charset_Range {
template <typename TYPE>
struct Charset1_2 {
- bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
+ bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs, unsigned *num_charset_entries) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this)))
return_trace (false);
num_glyphs--;
- for (unsigned int i = 0; num_glyphs > 0; i++)
+ unsigned i;
+ for (i = 0; num_glyphs > 0; i++)
{
if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1)))
return_trace (false);
num_glyphs -= (ranges[i].nLeft + 1);
}
+ if (num_charset_entries)
+ *num_charset_entries = i;
return_trace (true);
}
- hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const
+ hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs,
+ code_pair_t *cache = nullptr) const
{
if (unlikely (glyph >= num_glyphs)) return 0;
- if (glyph == 0) return 0;
- glyph--;
- for (unsigned int i = 0;; i++)
+ unsigned i;
+ hb_codepoint_t start_glyph;
+ if (cache && likely (cache->glyph <= glyph))
{
- if (glyph <= ranges[i].nLeft)
- return (hb_codepoint_t) ranges[i].first + glyph;
- glyph -= (ranges[i].nLeft + 1);
+ i = cache->code;
+ start_glyph = cache->glyph;
+ }
+ else
+ {
+ if (unlikely (glyph == 0)) return 0;
+ i = 0;
+ start_glyph = 1;
+ }
+ glyph -= start_glyph;
+ for (;; i++)
+ {
+ unsigned count = ranges[i].nLeft;
+ if (glyph <= count)
+ {
+ if (cache)
+ *cache = {i, start_glyph};
+ return ranges[i].first + glyph;
+ }
+ count++;
+ start_glyph += count;
+ glyph -= count;
}
return 0;
}
- void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+ void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const
{
+ mapping->resize (num_glyphs, false);
hb_codepoint_t gid = 1;
if (gid >= num_glyphs)
return;
@@ -413,8 +436,9 @@ struct Charset1_2 {
{
hb_codepoint_t sid = ranges[i].first;
unsigned count = ranges[i].nLeft + 1;
+ unsigned last = gid + count;
for (unsigned j = 0; j < count; j++)
- mapping->set (gid++, sid++);
+ mapping->arrayZ[gid++] = {sid++, last - 1};
if (gid >= num_glyphs)
break;
@@ -439,21 +463,26 @@ struct Charset1_2 {
unsigned int get_size (unsigned int num_glyphs) const
{
- unsigned int size = HBUINT8::static_size;
- int glyph = (int)num_glyphs;
+ int glyph = (int) num_glyphs;
+ unsigned num_ranges = 0;
assert (glyph > 0);
glyph--;
for (unsigned int i = 0; glyph > 0; i++)
{
glyph -= (ranges[i].nLeft + 1);
- size += Charset_Range<TYPE>::static_size;
+ num_ranges++;
}
- return size;
+ return get_size_for_ranges (num_ranges);
+ }
+
+ static unsigned int get_size_for_ranges (unsigned int num_ranges)
+ {
+ return UnsizedArrayOf<Charset_Range<TYPE> >::get_size (num_ranges);
}
- Charset_Range<TYPE> ranges[HB_VAR_ARRAY];
+ UnsizedArrayOf<Charset_Range<TYPE>> ranges;
DEFINE_SIZE_ARRAY (0, ranges);
};
@@ -469,11 +498,7 @@ struct Charset
bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs)
{
TRACE_SERIALIZE (this);
- unsigned int size = src.get_size (num_glyphs);
- Charset *dest = c->allocate_size<Charset> (size);
- if (unlikely (!dest)) return_trace (false);
- hb_memcpy (dest, &src, size);
- return_trace (true);
+ return_trace (c->embed ((const char *) &src, src.get_size (num_glyphs)));
}
/* serialize a subset Charset */
@@ -490,13 +515,13 @@ struct Charset
{
case 0:
{
- Charset0 *fmt0 = c->allocate_size<Charset0> (Charset0::min_size + HBUINT16::static_size * (num_glyphs - 1));
+ Charset0 *fmt0 = c->allocate_size<Charset0> (Charset0::get_size (num_glyphs), false);
if (unlikely (!fmt0)) return_trace (false);
unsigned int glyph = 0;
for (unsigned int i = 0; i < sid_ranges.length; i++)
{
- hb_codepoint_t sid = sid_ranges[i].code;
- for (int left = (int)sid_ranges[i].glyph; left >= 0; left--)
+ hb_codepoint_t sid = sid_ranges.arrayZ[i].code;
+ for (int left = (int)sid_ranges.arrayZ[i].glyph; left >= 0; left--)
fmt0->sids[glyph++] = sid++;
}
}
@@ -504,29 +529,35 @@ struct Charset
case 1:
{
- Charset1 *fmt1 = c->allocate_size<Charset1> (Charset1::min_size + Charset1_Range::static_size * sid_ranges.length);
+ Charset1 *fmt1 = c->allocate_size<Charset1> (Charset1::get_size_for_ranges (sid_ranges.length), false);
if (unlikely (!fmt1)) return_trace (false);
+ hb_codepoint_t all_glyphs = 0;
for (unsigned int i = 0; i < sid_ranges.length; i++)
{
- if (unlikely (!(sid_ranges[i].glyph <= 0xFF)))
- return_trace (false);
- fmt1->ranges[i].first = sid_ranges[i].code;
- fmt1->ranges[i].nLeft = sid_ranges[i].glyph;
+ auto &_ = sid_ranges.arrayZ[i];
+ all_glyphs |= _.glyph;
+ fmt1->ranges[i].first = _.code;
+ fmt1->ranges[i].nLeft = _.glyph;
}
+ if (unlikely (!(all_glyphs <= 0xFF)))
+ return_trace (false);
}
break;
case 2:
{
- Charset2 *fmt2 = c->allocate_size<Charset2> (Charset2::min_size + Charset2_Range::static_size * sid_ranges.length);
+ Charset2 *fmt2 = c->allocate_size<Charset2> (Charset2::get_size_for_ranges (sid_ranges.length), false);
if (unlikely (!fmt2)) return_trace (false);
+ hb_codepoint_t all_glyphs = 0;
for (unsigned int i = 0; i < sid_ranges.length; i++)
{
- if (unlikely (!(sid_ranges[i].glyph <= 0xFFFF)))
- return_trace (false);
- fmt2->ranges[i].first = sid_ranges[i].code;
- fmt2->ranges[i].nLeft = sid_ranges[i].glyph;
+ auto &_ = sid_ranges.arrayZ[i];
+ all_glyphs |= _.glyph;
+ fmt2->ranges[i].first = _.code;
+ fmt2->ranges[i].nLeft = _.glyph;
}
+ if (unlikely (!(all_glyphs <= 0xFFFF)))
+ return_trace (false);
}
break;
@@ -545,18 +576,19 @@ struct Charset
}
}
- hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs) const
+ hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs,
+ code_pair_t *cache = nullptr) const
{
switch (format)
{
case 0: return u.format0.get_sid (glyph, num_glyphs);
- case 1: return u.format1.get_sid (glyph, num_glyphs);
- case 2: return u.format2.get_sid (glyph, num_glyphs);
+ case 1: return u.format1.get_sid (glyph, num_glyphs, cache);
+ case 2: return u.format2.get_sid (glyph, num_glyphs, cache);
default:return 0;
}
}
- void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+ void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const
{
switch (format)
{
@@ -578,7 +610,7 @@ struct Charset
}
}
- bool sanitize (hb_sanitize_context_t *c) const
+ bool sanitize (hb_sanitize_context_t *c, unsigned *num_charset_entries) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this)))
@@ -586,9 +618,9 @@ struct Charset
switch (format)
{
- case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs ()));
- case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs ()));
- case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs ()));
+ case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs (), num_charset_entries));
+ case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs (), num_charset_entries));
+ case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs (), num_charset_entries));
default:return_trace (false);
}
}
@@ -606,10 +638,10 @@ struct Charset
struct CFF1StringIndex : CFF1Index
{
bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings,
- const hb_inc_bimap_t &sidmap)
+ const hb_vector_t<unsigned> &sidmap)
{
TRACE_SERIALIZE (this);
- if (unlikely ((strings.count == 0) || (sidmap.get_population () == 0)))
+ if (unlikely ((strings.count == 0) || (sidmap.length == 0)))
{
if (unlikely (!c->extend_min (this->count)))
return_trace (false);
@@ -617,15 +649,13 @@ struct CFF1StringIndex : CFF1Index
return_trace (true);
}
- byte_str_array_t bytesArray;
- if (!bytesArray.resize (sidmap.get_population ()))
- return_trace (false);
- for (unsigned int i = 0; i < strings.count; i++)
- {
- hb_codepoint_t j = sidmap[i];
- if (j != HB_MAP_VALUE_INVALID)
- bytesArray[j] = strings[i];
- }
+ if (unlikely (sidmap.in_error ())) return_trace (false);
+
+ // Save this in a vector since serialize() iterates it twice.
+ hb_vector_t<hb_ubytes_t> bytesArray (+ hb_iter (sidmap)
+ | hb_map (strings));
+
+ if (unlikely (bytesArray.in_error ())) return_trace (false);
bool result = CFF1Index::serialize (c, bytesArray);
return_trace (result);
@@ -932,7 +962,7 @@ struct cff1_private_dict_opset_t : dict_opset_t
}
};
-struct cff1_private_dict_opset_subset : dict_opset_t
+struct cff1_private_dict_opset_subset_t : dict_opset_t
{
static void process_op (op_code_t op, num_interp_env_t& env, cff1_private_dict_values_subset_t& dictval)
{
@@ -978,7 +1008,7 @@ typedef dict_interpreter_t<cff1_top_dict_opset_t, cff1_top_dict_values_t, cff1_t
typedef dict_interpreter_t<cff1_font_dict_opset_t, cff1_font_dict_values_t> cff1_font_dict_interpreter_t;
typedef CFF1Index CFF1NameIndex;
-typedef CFF1IndexOf<TopDict> CFF1TopDictIndex;
+typedef CFF1Index CFF1TopDictIndex;
struct cff1_font_dict_values_mod_t
{
@@ -1031,8 +1061,10 @@ struct cff1
template <typename PRIVOPSET, typename PRIVDICTVAL>
struct accelerator_templ_t
{
- void init (hb_face_t *face)
+ accelerator_templ_t (hb_face_t *face)
{
+ if (!face) return;
+
topDict.init ();
fontDicts.init ();
privateDicts.init ();
@@ -1046,22 +1078,22 @@ struct cff1
const OT::cff1 *cff = this->blob->template as<OT::cff1> ();
if (cff == &Null (OT::cff1))
- { fini (); return; }
+ goto fail;
nameIndex = &cff->nameIndex (cff);
if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc))
- { fini (); return; }
+ goto fail;
topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ());
if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0))
- { fini (); return; }
+ goto fail;
{ /* parse top dict */
const hb_ubytes_t topDictStr = (*topDictIndex)[0];
- if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!topDictStr.sanitize (&sc))) goto fail;
cff1_top_dict_interp_env_t env (topDictStr);
cff1_top_dict_interpreter_t top_interp (env);
- if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
+ if (unlikely (!top_interp.interpret (topDict))) goto fail;
}
if (is_predef_charset ())
@@ -1069,7 +1101,7 @@ struct cff1
else
{
charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset);
- if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc))) { fini (); return; }
+ if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc, &num_charset_entries))) goto fail;
}
fdCount = 1;
@@ -1079,7 +1111,7 @@ struct cff1
fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDict.FDSelectOffset);
if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) ||
(fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count)))
- { fini (); return; }
+ goto fail;
fdCount = fdArray->count;
}
@@ -1092,36 +1124,36 @@ struct cff1
encoding = &Null (Encoding);
if (is_CID ())
{
- if (unlikely (charset == &Null (Charset))) { fini (); return; }
+ if (unlikely (charset == &Null (Charset))) goto fail;
}
else
{
if (!is_predef_encoding ())
{
encoding = &StructAtOffsetOrNull<Encoding> (cff, topDict.EncodingOffset);
- if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; }
+ if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) goto fail;
}
}
stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ());
if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc))
- { fini (); return; }
+ goto fail;
globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ());
if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc))
- { fini (); return; }
+ goto fail;
charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset);
if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc)))
- { fini (); return; }
+ goto fail;
num_glyphs = charStrings->count;
if (num_glyphs != sc.get_num_glyphs ())
- { fini (); return; }
+ goto fail;
if (unlikely (!privateDicts.resize (fdCount)))
- { fini (); return; }
+ goto fail;
for (unsigned int i = 0; i < fdCount; i++)
privateDicts[i].init ();
@@ -1131,27 +1163,27 @@ struct cff1
for (unsigned int i = 0; i < fdCount; i++)
{
hb_ubytes_t fontDictStr = (*fdArray)[i];
- if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!fontDictStr.sanitize (&sc))) goto fail;
cff1_font_dict_values_t *font;
cff1_top_dict_interp_env_t env (fontDictStr);
cff1_font_dict_interpreter_t font_interp (env);
font = fontDicts.push ();
- if (unlikely (fontDicts.in_error ())) { fini (); return; }
+ if (unlikely (fontDicts.in_error ())) goto fail;
font->init ();
- if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
+ if (unlikely (!font_interp.interpret (*font))) goto fail;
PRIVDICTVAL *priv = &privateDicts[i];
const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size);
- if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!privDictStr.sanitize (&sc))) goto fail;
num_interp_env_t env2 (privDictStr);
dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env2);
priv->init ();
- if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+ if (unlikely (!priv_interp.interpret (*priv))) goto fail;
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset);
if (priv->localSubrs != &Null (CFF1Subrs) &&
unlikely (!priv->localSubrs->sanitize (&sc)))
- { fini (); return; }
+ goto fail;
}
}
else /* non-CID */
@@ -1160,20 +1192,25 @@ struct cff1
PRIVDICTVAL *priv = &privateDicts[0];
const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size);
- if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!privDictStr.sanitize (&sc))) goto fail;
num_interp_env_t env (privDictStr);
dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env);
priv->init ();
- if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+ if (unlikely (!priv_interp.interpret (*priv))) goto fail;
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset);
if (priv->localSubrs != &Null (CFF1Subrs) &&
unlikely (!priv->localSubrs->sanitize (&sc)))
- { fini (); return; }
+ goto fail;
}
- }
- void fini ()
+ return;
+
+ fail:
+ _fini ();
+ }
+ ~accelerator_templ_t () { _fini (); }
+ void _fini ()
{
sc.end_processing ();
topDict.fini ();
@@ -1183,6 +1220,8 @@ struct cff1
blob = nullptr;
}
+ hb_blob_t *get_blob () const { return blob; }
+
bool is_valid () const { return blob; }
bool is_CID () const { return topDict.is_CID (); }
@@ -1203,13 +1242,14 @@ struct cff1
bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; }
- hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const
+ hb_codepoint_t glyph_to_code (hb_codepoint_t glyph,
+ code_pair_t *glyph_to_sid_cache = nullptr) const
{
if (encoding != &Null (Encoding))
return encoding->get_code (glyph);
else
{
- hb_codepoint_t sid = glyph_to_sid (glyph);
+ hb_codepoint_t sid = glyph_to_sid (glyph, glyph_to_sid_cache);
if (sid == 0) return 0;
hb_codepoint_t code = 0;
switch (topDict.EncodingOffset)
@@ -1227,12 +1267,14 @@ struct cff1
}
}
- hb_map_t *create_glyph_to_sid_map () const
+ glyph_to_sid_map_t *create_glyph_to_sid_map () const
{
if (charset != &Null (Charset))
{
- hb_map_t *mapping = hb_map_create ();
- mapping->set (0, 0);
+ auto *mapping = (glyph_to_sid_map_t *) hb_malloc (sizeof (glyph_to_sid_map_t));
+ if (unlikely (!mapping)) return nullptr;
+ mapping = new (mapping) glyph_to_sid_map_t ();
+ mapping->push (code_pair_t {0, 1});
charset->collect_glyph_to_sid_map (mapping, num_glyphs);
return mapping;
}
@@ -1240,10 +1282,11 @@ struct cff1
return nullptr;
}
- hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const
+ hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph,
+ code_pair_t *cache = nullptr) const
{
if (charset != &Null (Charset))
- return charset->get_sid (glyph, num_glyphs);
+ return charset->get_sid (glyph, num_glyphs, cache);
else
{
hb_codepoint_t sid = 0;
@@ -1312,19 +1355,17 @@ struct cff1
hb_vector_t<PRIVDICTVAL> privateDicts;
unsigned int num_glyphs = 0;
+ unsigned int num_charset_entries = 0;
};
struct accelerator_t : accelerator_templ_t<cff1_private_dict_opset_t, cff1_private_dict_values_t>
{
- accelerator_t (hb_face_t *face)
+ accelerator_t (hb_face_t *face) : SUPER (face)
{
- SUPER::init (face);
-
glyph_names.set_relaxed (nullptr);
if (!is_valid ()) return;
if (is_CID ()) return;
-
}
~accelerator_t ()
{
@@ -1334,8 +1375,6 @@ struct cff1
names->fini ();
hb_free (names);
}
-
- SUPER::fini ();
}
bool get_glyph_name (hb_codepoint_t glyph,
@@ -1386,9 +1425,10 @@ struct cff1
/* TODO */
/* fill glyph names */
+ code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID};
for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
{
- hb_codepoint_t sid = glyph_to_sid (gid);
+ hb_codepoint_t sid = glyph_to_sid (gid, &glyph_to_sid_cache);
gname_t gname;
gname.sid = sid;
if (sid < cff1_std_strings_length)
@@ -1426,7 +1466,6 @@ struct cff1
HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const;
HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
- HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const;
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
private:
@@ -1453,9 +1492,24 @@ struct cff1
typedef accelerator_templ_t<cff1_private_dict_opset_t, cff1_private_dict_values_t> SUPER;
};
- struct accelerator_subset_t : accelerator_templ_t<cff1_private_dict_opset_subset, cff1_private_dict_values_subset_t> {};
+ struct accelerator_subset_t : accelerator_templ_t<cff1_private_dict_opset_subset_t, cff1_private_dict_values_subset_t>
+ {
+ accelerator_subset_t (hb_face_t *face) : SUPER (face) {}
+ ~accelerator_subset_t ()
+ {
+ if (cff_accelerator)
+ cff_subset_accelerator_t::destroy (cff_accelerator);
+ }
+
+ HB_INTERNAL bool subset (hb_subset_context_t *c) const;
+ HB_INTERNAL bool serialize (hb_serialize_context_t *c,
+ struct cff1_subset_plan &plan) const;
+ HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const;
+
+ mutable CFF::cff_subset_accelerator_t* cff_accelerator = nullptr;
- bool subset (hb_subset_context_t *c) const { return hb_subset_cff1 (c); }
+ typedef accelerator_templ_t<cff1_private_dict_opset_subset_t, cff1_private_dict_values_subset_t> SUPER;
+ };
protected:
HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid);
@@ -1479,6 +1533,10 @@ struct cff1_accelerator_t : cff1::accelerator_t {
cff1_accelerator_t (hb_face_t *face) : cff1::accelerator_t (face) {}
};
+struct cff1_subset_accelerator_t : cff1::accelerator_subset_t {
+ cff1_subset_accelerator_t (hb_face_t *face) : cff1::accelerator_subset_t (face) {}
+};
+
} /* namespace OT */
#endif /* HB_OT_CFF1_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh
index 2134d48660..9913cdad0c 100644
--- a/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh
@@ -28,7 +28,7 @@
#define HB_OT_CFF2_TABLE_HH
#include "hb-ot-cff-common.hh"
-#include "hb-subset-cff2.hh"
+#include "hb-subset-cff-common.hh"
#include "hb-draw.hh"
#include "hb-paint.hh"
@@ -41,7 +41,6 @@ namespace CFF {
#define HB_OT_TAG_CFF2 HB_TAG('C','F','F','2')
typedef CFFIndex<HBUINT32> CFF2Index;
-template <typename Type> struct CFF2IndexOf : CFFIndexOf<HBUINT32, Type> {};
typedef CFF2Index CFF2CharStrings;
typedef Subrs<HBUINT32> CFF2Subrs;
@@ -393,6 +392,8 @@ struct cff2
{
accelerator_templ_t (hb_face_t *face)
{
+ if (!face) return;
+
topDict.init ();
fontDicts.init ();
privateDicts.init ();
@@ -464,7 +465,6 @@ struct cff2
goto fail;
}
-
return;
fail:
@@ -481,11 +481,13 @@ struct cff2
blob = nullptr;
}
- hb_map_t *create_glyph_to_sid_map () const
+ hb_vector_t<uint16_t> *create_glyph_to_sid_map () const
{
return nullptr;
}
+ hb_blob_t *get_blob () const { return blob; }
+
bool is_valid () const { return blob; }
protected:
@@ -518,9 +520,24 @@ struct cff2
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
};
- typedef accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t> accelerator_subset_t;
+ struct accelerator_subset_t : accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t>
+ {
+ accelerator_subset_t (hb_face_t *face) : SUPER (face) {}
+ ~accelerator_subset_t ()
+ {
+ if (cff_accelerator)
+ cff_subset_accelerator_t::destroy (cff_accelerator);
+ }
- bool subset (hb_subset_context_t *c) const { return hb_subset_cff2 (c); }
+ HB_INTERNAL bool subset (hb_subset_context_t *c) const;
+ HB_INTERNAL bool serialize (hb_serialize_context_t *c,
+ struct cff2_subset_plan &plan,
+ hb_array_t<int> normalized_coords) const;
+
+ mutable CFF::cff_subset_accelerator_t* cff_accelerator = nullptr;
+
+ typedef accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t> SUPER;
+ };
public:
FixedVersion<HBUINT8> version; /* Version of CFF2 table. set to 0x0200u */
@@ -535,6 +552,10 @@ struct cff2_accelerator_t : cff2::accelerator_t {
cff2_accelerator_t (hb_face_t *face) : cff2::accelerator_t (face) {}
};
+struct cff2_subset_accelerator_t : cff2::accelerator_subset_t {
+ cff2_subset_accelerator_t (hb_face_t *face) : cff2::accelerator_subset_t (face) {}
+};
+
} /* namespace OT */
#endif /* HB_OT_CFF2_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh
index cf5ccd53e9..30401b1926 100644
--- a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh
@@ -277,10 +277,10 @@ struct CmapSubtableFormat4
}
} writer(c);
- writer.end_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount);
- c->allocate_size<HBUINT16> (2); // padding
- writer.start_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount);
- writer.id_delta_ = c->allocate_size<HBINT16> (HBINT16::static_size * segcount);
+ writer.end_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount, false);
+ (void) c->allocate_size<HBUINT16> (2); // padding
+ writer.start_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount, false);
+ writer.id_delta_ = c->allocate_size<HBINT16> (HBINT16::static_size * segcount, false);
if (unlikely (!writer.end_code_ || !writer.start_code_ || !writer.id_delta_)) return false;
@@ -325,7 +325,7 @@ struct CmapSubtableFormat4
{
auto format4_iter =
+ it
- | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
+ | hb_filter ([&] (const hb_codepoint_pair_t _)
{ return _.first <= 0xFFFF; })
;
@@ -335,7 +335,7 @@ struct CmapSubtableFormat4
if (unlikely (!c->extend_min (this))) return;
this->format = 4;
- hb_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> cp_to_gid {
+ hb_vector_t<hb_codepoint_pair_t> cp_to_gid {
format4_iter
};
@@ -757,8 +757,7 @@ struct CmapSubtableLongSegmented
hb_codepoint_t gid = this->groups[i].glyphID;
if (!gid)
{
- /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */
- if (! T::group_get_glyph (this->groups[i], end)) continue;
+ if (T::formatNumber == 13) continue;
start++;
gid++;
}
@@ -766,11 +765,13 @@ struct CmapSubtableLongSegmented
if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs))
end = start + (hb_codepoint_t) num_glyphs - gid;
+ mapping->alloc (mapping->get_population () + end - start + 1);
+
for (unsigned cp = start; cp <= end; cp++)
{
unicodes->add (cp);
mapping->set (cp, gid);
- gid++;
+ gid += T::increment;
}
}
}
@@ -794,6 +795,9 @@ struct CmapSubtableLongSegmented
struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
{
+ static constexpr int increment = 1;
+ static constexpr int formatNumber = 12;
+
static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
hb_codepoint_t u)
{ return likely (group.startCharCode <= group.endCharCode) ?
@@ -866,6 +870,9 @@ struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13>
{
+ static constexpr int increment = 0;
+ static constexpr int formatNumber = 13;
+
static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
hb_codepoint_t u HB_UNUSED)
{ return group.glyphID; }
@@ -917,8 +924,7 @@ struct DefaultUVS : SortedArray32Of<UnicodeValueRange>
DefaultUVS* copy (hb_serialize_context_t *c,
const hb_set_t *unicodes) const
{
- DefaultUVS *out = c->start_embed<DefaultUVS> ();
- if (unlikely (!out)) return nullptr;
+ auto *out = c->start_embed<DefaultUVS> ();
auto snap = c->snapshot ();
HBUINT32 len;
@@ -931,8 +937,7 @@ struct DefaultUVS : SortedArray32Of<UnicodeValueRange>
hb_codepoint_t start = HB_SET_VALUE_INVALID;
hb_codepoint_t end = HB_SET_VALUE_INVALID;
- for (hb_codepoint_t u = HB_SET_VALUE_INVALID;
- unicodes->next (&u);)
+ for (auto u : *unicodes)
{
if (!as_array ().bsearch (u))
continue;
@@ -1067,9 +1072,7 @@ struct NonDefaultUVS : SortedArray32Of<UVSMapping>
const hb_set_t *glyphs_requested,
const hb_map_t *glyph_map) const
{
- NonDefaultUVS *out = c->start_embed<NonDefaultUVS> ();
- if (unlikely (!out)) return nullptr;
-
+ auto *out = c->start_embed<NonDefaultUVS> ();
auto it =
+ as_array ()
| hb_filter ([&] (const UVSMapping& _)
@@ -1767,7 +1770,6 @@ struct cmap
TRACE_SUBSET (this);
cmap *cmap_prime = c->serializer->start_embed<cmap> ();
- if (unlikely (!c->serializer->check_success (cmap_prime))) return_trace (false);
auto encodingrec_iter =
+ hb_iter (encodingRecord)
@@ -1798,7 +1800,7 @@ struct cmap
auto it =
+ c->plan->unicode_to_new_gid_list.iter ()
- | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
+ | hb_filter ([&] (const hb_codepoint_pair_t _)
{ return (_.second != HB_MAP_VALUE_INVALID); })
;
diff --git a/thirdparty/harfbuzz/src/hb-ot-font.cc b/thirdparty/harfbuzz/src/hb-ot-font.cc
index c89a1954a9..b3677c6a4c 100644
--- a/thirdparty/harfbuzz/src/hb-ot-font.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-font.cc
@@ -38,8 +38,8 @@
#include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh"
-#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
+#include "hb-ot-cff1-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-post-table.hh"
#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise.
@@ -98,7 +98,7 @@ _hb_ot_font_create (hb_font_t *font)
{
cmap_cache = (hb_ot_font_cmap_cache_t *) hb_malloc (sizeof (hb_ot_font_cmap_cache_t));
if (unlikely (!cmap_cache)) goto out;
- cmap_cache->init ();
+ new (cmap_cache) hb_ot_font_cmap_cache_t ();
if (unlikely (!hb_face_set_user_data (font->face,
&hb_ot_font_cmap_cache_user_data_key,
cmap_cache,
@@ -230,8 +230,8 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
use_cache = false;
goto out;
}
+ new (cache) hb_ot_font_advance_cache_t;
- cache->init ();
if (unlikely (!ot_font->advance_cache.cmpexch (nullptr, cache)))
{
hb_free (cache);
@@ -255,7 +255,7 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
{ /* Use cache. */
if (ot_font->cached_coords_serial.get_acquire () != (int) font->serial_coords)
{
- ot_font->advance_cache->init ();
+ ot_font->advance_cache->clear ();
ot_font->cached_coords_serial.set_release (font->serial_coords);
}
@@ -436,8 +436,8 @@ hb_ot_get_glyph_extents (hb_font_t *font,
#endif
if (ot_face->glyf->get_extents (font, glyph, extents)) return true;
#ifndef HB_NO_OT_FONT_CFF
- if (ot_face->cff1->get_extents (font, glyph, extents)) return true;
if (ot_face->cff2->get_extents (font, glyph, extents)) return true;
+ if (ot_face->cff1->get_extents (font, glyph, extents)) return true;
#endif
return false;
@@ -525,8 +525,8 @@ hb_ot_draw_glyph (hb_font_t *font,
embolden ? &outline : draw_data, font->slant_xy);
if (!font->face->table.glyf->get_path (font, glyph, draw_session))
#ifndef HB_NO_CFF
- if (!font->face->table.cff1->get_path (font, glyph, draw_session))
if (!font->face->table.cff2->get_path (font, glyph, draw_session))
+ if (!font->face->table.cff1->get_path (font, glyph, draw_session))
#endif
{}
}
@@ -565,8 +565,8 @@ hb_ot_paint_glyph (hb_font_t *font,
#endif
if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
#ifndef HB_NO_CFF
- if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
+ if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
#endif
}
#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh b/thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh
index 3bfd75502a..cbcf6f5f22 100644
--- a/thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh
@@ -46,21 +46,23 @@ struct DeviceRecord
template<typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
- bool serialize (hb_serialize_context_t *c, unsigned pixelSize, Iterator it)
+ bool serialize (hb_serialize_context_t *c,
+ unsigned pixelSize,
+ Iterator it,
+ const hb_vector_t<hb_codepoint_pair_t> new_to_old_gid_list,
+ unsigned num_glyphs)
{
TRACE_SERIALIZE (this);
- unsigned length = it.len ();
-
- if (unlikely (!c->extend (this, length))) return_trace (false);
+ if (unlikely (!c->extend (this, num_glyphs))) return_trace (false);
this->pixelSize = pixelSize;
this->maxWidth =
+ it
| hb_reduce (hb_max, 0u);
- + it
- | hb_sink (widthsZ.as_array (length));
+ for (auto &_ : new_to_old_gid_list)
+ widthsZ[_.first] = *it++;
return_trace (true);
}
@@ -89,7 +91,11 @@ struct hdmx
template<typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
- bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it)
+ bool serialize (hb_serialize_context_t *c,
+ unsigned version,
+ Iterator it,
+ const hb_vector_t<hb_codepoint_pair_t> &new_to_old_gid_list,
+ unsigned num_glyphs)
{
TRACE_SERIALIZE (this);
@@ -97,10 +103,10 @@ struct hdmx
this->version = version;
this->numRecords = it.len ();
- this->sizeDeviceRecord = DeviceRecord::get_size (it ? (*it).second.len () : 0);
+ this->sizeDeviceRecord = DeviceRecord::get_size (num_glyphs);
for (const hb_item_type<Iterator>& _ : +it)
- c->start_embed<DeviceRecord> ()->serialize (c, _.first, _.second);
+ c->start_embed<DeviceRecord> ()->serialize (c, _.first, _.second, new_to_old_gid_list, num_glyphs);
return_trace (c->successful ());
}
@@ -110,31 +116,30 @@ struct hdmx
{
TRACE_SUBSET (this);
- hdmx *hdmx_prime = c->serializer->start_embed <hdmx> ();
- if (unlikely (!hdmx_prime)) return_trace (false);
+ auto *hdmx_prime = c->serializer->start_embed <hdmx> ();
+ unsigned num_input_glyphs = get_num_glyphs ();
auto it =
+ hb_range ((unsigned) numRecords)
- | hb_map ([c, this] (unsigned _)
+ | hb_map ([c, num_input_glyphs, this] (unsigned _)
{
const DeviceRecord *device_record =
&StructAtOffset<DeviceRecord> (&firstDeviceRecord,
_ * sizeDeviceRecord);
auto row =
- + hb_range (c->plan->num_output_glyphs ())
- | hb_map (c->plan->reverse_glyph_map)
- | hb_map ([this, c, device_record] (hb_codepoint_t _)
+ + hb_iter (c->plan->new_to_old_gid_list)
+ | hb_map ([num_input_glyphs, device_record] (hb_codepoint_pair_t _)
{
- if (c->plan->is_empty_glyph (_))
- return Null (HBUINT8);
- return device_record->widthsZ.as_array (get_num_glyphs ()) [_];
+ return device_record->widthsZ.as_array (num_input_glyphs) [_.second];
})
;
return hb_pair ((unsigned) device_record->pixelSize, +row);
})
;
- hdmx_prime->serialize (c->serializer, version, it);
+ hdmx_prime->serialize (c->serializer, version, it,
+ c->plan->new_to_old_gid_list,
+ c->plan->num_output_glyphs ());
return_trace (true);
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh b/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh
index 835a1a585e..fed6ca1c7a 100644
--- a/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh
@@ -83,7 +83,7 @@ struct hmtxvmtx
bool subset_update_header (hb_subset_context_t *c,
unsigned int num_hmetrics,
const hb_hashmap_t<hb_codepoint_t, hb_pair_t<unsigned, int>> *mtx_map,
- const hb_map_t *bounds_map) const
+ const hb_vector_t<unsigned> &bounds_vec) const
{
hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table<H> (c->plan->source, H::tableTag);
hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob);
@@ -114,6 +114,7 @@ struct hmtxvmtx
HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET, caretOffset);
}
+ bool empty = true;
int min_lsb = 0x7FFF;
int min_rsb = 0x7FFF;
int max_extent = -0x7FFF;
@@ -125,9 +126,10 @@ struct hmtxvmtx
int lsb = _.second.second;
max_adv = hb_max (max_adv, adv);
- if (bounds_map->has (gid))
+ if (bounds_vec[gid] != 0xFFFFFFFF)
{
- unsigned bound_width = bounds_map->get (gid);
+ empty = false;
+ unsigned bound_width = bounds_vec[gid];
int rsb = adv - lsb - bound_width;
int extent = lsb + bound_width;
min_lsb = hb_min (min_lsb, lsb);
@@ -137,7 +139,7 @@ struct hmtxvmtx
}
table->advanceMax = max_adv;
- if (!bounds_map->is_empty ())
+ if (!empty)
{
table->minLeadingBearing = min_lsb;
table->minTrailingBearing = min_rsb;
@@ -156,32 +158,31 @@ struct hmtxvmtx
hb_requires (hb_is_iterator (Iterator))>
void serialize (hb_serialize_context_t *c,
Iterator it,
- unsigned num_long_metrics)
+ const hb_vector_t<hb_codepoint_pair_t> new_to_old_gid_list,
+ unsigned num_long_metrics,
+ unsigned total_num_metrics)
{
- unsigned idx = 0;
- for (auto _ : it)
+ LongMetric* long_metrics = c->allocate_size<LongMetric> (num_long_metrics * LongMetric::static_size);
+ FWORD* short_metrics = c->allocate_size<FWORD> ((total_num_metrics - num_long_metrics) * FWORD::static_size);
+ if (!long_metrics || !short_metrics) return;
+
+ short_metrics -= num_long_metrics;
+
+ for (auto _ : new_to_old_gid_list)
{
- if (idx < num_long_metrics)
- {
- LongMetric lm;
- lm.advance = _.first;
- lm.sb = _.second;
- if (unlikely (!c->embed<LongMetric> (&lm))) return;
- }
- else if (idx < 0x10000u)
+ hb_codepoint_t gid = _.first;
+ auto mtx = *it++;
+
+ if (gid < num_long_metrics)
{
- FWORD *sb = c->allocate_size<FWORD> (FWORD::static_size);
- if (unlikely (!sb)) return;
- *sb = _.second;
+ LongMetric& lm = long_metrics[gid];
+ lm.advance = mtx.first;
+ lm.sb = mtx.second;
}
+ else if (gid < 0x10000u)
+ short_metrics[gid] = mtx.second;
else
- {
- // TODO: This does not do tail optimization.
- UFWORD *adv = c->allocate_size<UFWORD> (UFWORD::static_size);
- if (unlikely (!adv)) return;
- *adv = _.first;
- }
- idx++;
+ ((UFWORD*) short_metrics)[gid] = mtx.first;
}
}
@@ -189,8 +190,7 @@ struct hmtxvmtx
{
TRACE_SUBSET (this);
- T *table_prime = c->serializer->start_embed <T> ();
- if (unlikely (!table_prime)) return_trace (false);
+ auto *table_prime = c->serializer->start_embed <T> ();
accelerator_t _mtx (c->plan->source);
unsigned num_long_metrics;
@@ -209,31 +209,36 @@ struct hmtxvmtx
}
auto it =
- + hb_range (c->plan->num_output_glyphs ())
- | hb_map ([c, &_mtx, mtx_map] (unsigned _)
+ + hb_iter (c->plan->new_to_old_gid_list)
+ | hb_map ([c, &_mtx, mtx_map] (hb_codepoint_pair_t _)
{
- if (!mtx_map->has (_))
+ hb_codepoint_t new_gid = _.first;
+ hb_codepoint_t old_gid = _.second;
+
+ hb_pair_t<unsigned, int> *v = nullptr;
+ if (!mtx_map->has (new_gid, &v))
{
- hb_codepoint_t old_gid;
- if (!c->plan->old_gid_for_new_gid (_, &old_gid))
- return hb_pair (0u, 0);
int lsb = 0;
if (!_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
(void) _glyf_get_leading_bearing_without_var_unscaled (c->plan->source, old_gid, !T::is_horizontal, &lsb);
return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb);
}
- return mtx_map->get (_);
+ return *v;
})
;
- table_prime->serialize (c->serializer, it, num_long_metrics);
+ table_prime->serialize (c->serializer,
+ it,
+ c->plan->new_to_old_gid_list,
+ num_long_metrics,
+ c->plan->num_output_glyphs ());
if (unlikely (c->serializer->in_error ()))
return_trace (false);
// Amend header num hmetrics
if (unlikely (!subset_update_header (c, num_long_metrics, mtx_map,
- T::is_horizontal ? &c->plan->bounds_width_map : &c->plan->bounds_height_map)))
+ T::is_horizontal ? c->plan->bounds_width_vec : c->plan->bounds_height_vec)))
return_trace (false);
return_trace (true);
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh
index 8179e5acd5..2b7e9e4b18 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh
@@ -170,8 +170,8 @@ struct FeatMinMaxRecord
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
- minCoord.sanitize (c, this) &&
- maxCoord.sanitize (c, this)));
+ minCoord.sanitize (c, base) &&
+ maxCoord.sanitize (c, base)));
}
protected:
@@ -187,7 +187,6 @@ struct FeatMinMaxRecord
* of MinMax table (may be NULL) */
public:
DEFINE_SIZE_STATIC (8);
-
};
struct MinMax
@@ -274,7 +273,7 @@ struct BaseLangSysRecord
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
- minMax.sanitize (c, this)));
+ minMax.sanitize (c, base)));
}
protected:
@@ -297,7 +296,8 @@ struct BaseScript
const BaseCoord &get_base_coord (int baseline_tag_index) const
{ return (this+baseValues).get_base_coord (baseline_tag_index); }
- bool has_data () const { return baseValues; }
+ bool has_values () const { return baseValues; }
+ bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ }
bool sanitize (hb_sanitize_context_t *c) const
{
@@ -383,7 +383,7 @@ struct Axis
const BaseCoord **coord) const
{
const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
- if (!base_script.has_data ())
+ if (!base_script.has_values ())
{
*coord = nullptr;
return false;
@@ -410,7 +410,7 @@ struct Axis
const BaseCoord **max_coord) const
{
const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
- if (!base_script.has_data ())
+ if (!base_script.has_min_max ())
{
*min_coord = *max_coord = nullptr;
return false;
@@ -425,8 +425,8 @@ struct Axis
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
- (this+baseTagList).sanitize (c) &&
- (this+baseScriptList).sanitize (c)));
+ baseTagList.sanitize (c, this) &&
+ baseScriptList.sanitize (c, this)));
}
protected:
@@ -473,14 +473,13 @@ struct BASE
return true;
}
- /* TODO: Expose this separately sometime? */
bool get_min_max (hb_font_t *font,
hb_direction_t direction,
hb_tag_t script_tag,
hb_tag_t language_tag,
hb_tag_t feature_tag,
hb_position_t *min,
- hb_position_t *max)
+ hb_position_t *max) const
{
const BaseCoord *min_coord, *max_coord;
if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh
index 36f123b559..b3af128e02 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh
@@ -55,19 +55,22 @@ static bool ClassDef_remap_and_serialize (
hb_serialize_context_t *c,
const hb_set_t &klasses,
bool use_class_zero,
- hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */
+ hb_sorted_vector_t<hb_codepoint_pair_t> &glyph_and_klass, /* IN/OUT */
hb_map_t *klass_map /*IN/OUT*/);
struct hb_collect_feature_substitutes_with_var_context_t
{
const hb_map_t *axes_index_tag_map;
- const hb_hashmap_t<hb_tag_t, int> *axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple> *axes_location;
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *record_cond_idx_map;
hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map;
+ bool& insert_catch_all_feature_variation_record;
// not stored in subset_plan
hb_set_t *feature_indices;
bool apply;
+ bool variation_applied;
+ bool universal;
unsigned cur_record_idx;
hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> *conditionset_map;
};
@@ -807,7 +810,7 @@ struct Feature
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->featureParams.serialize_subset (c, featureParams, this, tag);
@@ -981,7 +984,7 @@ struct RecordListOfFeature : RecordListOf<Feature>
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ hb_enumerate (*this)
| hb_filter (l->feature_index_map, hb_first)
@@ -1078,7 +1081,7 @@ struct LangSys
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
const uint32_t *v;
out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex, &v) ? *v : 0xFFFFu;
@@ -1188,7 +1191,7 @@ struct Script
return false;
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
bool defaultLang = false;
if (has_default_lang_sys ())
@@ -1247,7 +1250,7 @@ struct RecordListOfScript : RecordListOf<Script>
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
for (auto _ : + hb_enumerate (*this))
{
@@ -1367,7 +1370,7 @@ struct Lookup
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->lookupType = lookupType;
out->lookupFlag = lookupFlag;
@@ -1456,7 +1459,7 @@ struct LookupOffsetList : List16OfOffsetTo<TLookup, OffsetType>
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ hb_enumerate (*this)
| hb_filter (l->lookup_index_map, hb_first)
@@ -1482,7 +1485,7 @@ struct LookupOffsetList : List16OfOffsetTo<TLookup, OffsetType>
static bool ClassDef_remap_and_serialize (hb_serialize_context_t *c,
const hb_set_t &klasses,
bool use_class_zero,
- hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */
+ hb_sorted_vector_t<hb_codepoint_pair_t> &glyph_and_klass, /* IN/OUT */
hb_map_t *klass_map /*IN/OUT*/)
{
if (!klass_map)
@@ -1573,7 +1576,7 @@ struct ClassDefFormat1_3
TRACE_SUBSET (this);
const hb_map_t &glyph_map = c->plan->glyph_map_gsub;
- hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass;
+ hb_sorted_vector_t<hb_codepoint_pair_t> glyph_and_klass;
hb_set_t orig_klasses;
hb_codepoint_t start = startGlyph;
@@ -1592,10 +1595,13 @@ struct ClassDefFormat1_3
orig_klasses.add (klass);
}
- unsigned glyph_count = glyph_filter
- ? hb_len (hb_iter (glyph_map.keys()) | hb_filter (glyph_filter))
- : glyph_map.get_population ();
- use_class_zero = use_class_zero && glyph_count <= glyph_and_klass.length;
+ if (use_class_zero)
+ {
+ unsigned glyph_count = glyph_filter
+ ? hb_len (hb_iter (glyph_map.keys()) | hb_filter (glyph_filter))
+ : glyph_map.get_population ();
+ use_class_zero = glyph_count <= glyph_and_klass.length;
+ }
if (!ClassDef_remap_and_serialize (c->serializer,
orig_klasses,
use_class_zero,
@@ -1830,7 +1836,7 @@ struct ClassDefFormat2_4
const hb_map_t &glyph_map = c->plan->glyph_map_gsub;
const hb_set_t &glyph_set = *c->plan->glyphset_gsub ();
- hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass;
+ hb_sorted_vector_t<hb_codepoint_pair_t> glyph_and_klass;
hb_set_t orig_klasses;
if (glyph_set.get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2
@@ -1916,7 +1922,7 @@ struct ClassDefFormat2_4
{
if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2)
{
- for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
+ for (auto g : *glyphs)
if (get_class (g))
return true;
return false;
@@ -1976,8 +1982,7 @@ struct ClassDefFormat2_4
unsigned count = rangeRecord.len;
if (count > glyphs->get_population () * hb_bit_storage (count) * 8)
{
- for (hb_codepoint_t g = HB_SET_VALUE_INVALID;
- glyphs->next (&g);)
+ for (auto g : *glyphs)
{
unsigned i;
if (rangeRecord.as_array ().bfind (g, &i) &&
@@ -2377,7 +2382,7 @@ struct VarRegionList
return_trace (c->check_struct (this) && axesZ.sanitize (c, axisCount * regionCount));
}
- bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_bimap_t &region_map)
+ bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_inc_bimap_t &region_map)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (this))) return_trace (false);
@@ -2494,7 +2499,7 @@ struct VarData
bool serialize (hb_serialize_context_t *c,
const VarData *src,
const hb_inc_bimap_t &inner_map,
- const hb_bimap_t &region_map)
+ const hb_inc_bimap_t &region_map)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (this))) return_trace (false);
@@ -2905,9 +2910,9 @@ struct VariationStore
enum Cond_with_Var_flag_t
{
KEEP_COND_WITH_VAR = 0,
- DROP_COND_WITH_VAR = 1,
- DROP_RECORD_WITH_VAR = 2,
- MEM_ERR_WITH_VAR = 3,
+ KEEP_RECORD_WITH_VAR = 1,
+ DROP_COND_WITH_VAR = 2,
+ DROP_RECORD_WITH_VAR = 3,
};
struct ConditionFormat1
@@ -2940,29 +2945,42 @@ struct ConditionFormat1
hb_tag_t axis_tag = c->axes_index_tag_map->get (axisIndex);
- //axis not pinned, keep the condition
- if (!c->axes_location->has (axis_tag))
+ Triple axis_range (-1.f, 0.f, 1.f);
+ if (c->axes_location->has (axis_tag))
+ axis_range = c->axes_location->get (axis_tag);
+
+ int axis_min_val = axis_range.minimum;
+ int axis_default_val = axis_range.middle;
+ int axis_max_val = axis_range.maximum;
+
+ int16_t filter_min_val = filterRangeMinValue.to_int ();
+ int16_t filter_max_val = filterRangeMaxValue.to_int ();
+
+ if (axis_default_val < filter_min_val ||
+ axis_default_val > filter_max_val)
+ c->apply = false;
+
+ //condition not met, drop the entire record
+ if (axis_min_val > filter_max_val || axis_max_val < filter_min_val ||
+ filter_min_val > filter_max_val)
+ return DROP_RECORD_WITH_VAR;
+
+ //condition met and axis pinned, drop the condition
+ if (c->axes_location->has (axis_tag) &&
+ c->axes_location->get (axis_tag).is_point ())
+ return DROP_COND_WITH_VAR;
+
+ if (filter_max_val != axis_max_val || filter_min_val != axis_min_val)
{
// add axisIndex->value into the hashmap so we can check if the record is
// unique with variations
- int16_t min_val = filterRangeMinValue.to_int ();
- int16_t max_val = filterRangeMaxValue.to_int ();
- hb_codepoint_t val = (max_val << 16) + min_val;
+ hb_codepoint_t val = (filter_max_val << 16) + filter_min_val;
condition_map->set (axisIndex, val);
return KEEP_COND_WITH_VAR;
}
- //axis pinned, check if condition is met
- //TODO: add check for axis Ranges
- int v = c->axes_location->get (axis_tag);
-
- //condition not met, drop the entire record
- if (v < filterRangeMinValue.to_int () || v > filterRangeMaxValue.to_int ())
- return DROP_RECORD_WITH_VAR;
-
- //axis pinned and condition met, drop the condition
- return DROP_COND_WITH_VAR;
+ return KEEP_RECORD_WITH_VAR;
}
bool evaluate (const int *coords, unsigned int coord_len) const
@@ -3001,7 +3019,7 @@ struct Condition
{
switch (u.format) {
case 1: return u.format1.keep_with_variations (c, condition_map);
- default:return KEEP_COND_WITH_VAR;
+ default: c->apply = false; return KEEP_COND_WITH_VAR;
}
}
@@ -3046,45 +3064,50 @@ struct ConditionSet
return true;
}
- Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
+ void keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
{
hb_map_t *condition_map = hb_map_create ();
- if (unlikely (!condition_map)) return MEM_ERR_WITH_VAR;
+ if (unlikely (!condition_map)) return;
hb::shared_ptr<hb_map_t> p {condition_map};
hb_set_t *cond_set = hb_set_create ();
- if (unlikely (!cond_set)) return MEM_ERR_WITH_VAR;
+ if (unlikely (!cond_set)) return;
hb::shared_ptr<hb_set_t> s {cond_set};
+ c->apply = true;
+ bool should_keep = false;
unsigned num_kept_cond = 0, cond_idx = 0;
for (const auto& offset : conditions)
{
Cond_with_Var_flag_t ret = (this+offset).keep_with_variations (c, condition_map);
- // one condition is not met, drop the entire record
+ // condition is not met or condition out of range, drop the entire record
if (ret == DROP_RECORD_WITH_VAR)
- return DROP_RECORD_WITH_VAR;
+ return;
- // axis not pinned, keep this condition
if (ret == KEEP_COND_WITH_VAR)
{
+ should_keep = true;
cond_set->add (cond_idx);
num_kept_cond++;
}
+
+ if (ret == KEEP_RECORD_WITH_VAR)
+ should_keep = true;
+
cond_idx++;
}
- // all conditions met
- if (num_kept_cond == 0) return DROP_COND_WITH_VAR;
+ if (!should_keep) return;
//check if condition_set is unique with variations
if (c->conditionset_map->has (p))
//duplicate found, drop the entire record
- return DROP_RECORD_WITH_VAR;
+ return;
c->conditionset_map->set (p, 1);
c->record_cond_idx_map->set (c->cur_record_idx, s);
-
- return KEEP_COND_WITH_VAR;
+ if (should_keep && num_kept_cond == 0)
+ c->universal = true;
}
bool subset (hb_subset_context_t *c,
@@ -3289,12 +3312,11 @@ struct FeatureVariationRecord
void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c,
const void *base) const
{
- // ret == 1, all conditions met
- if ((base+conditions).keep_with_variations (c) == DROP_COND_WITH_VAR &&
- c->apply)
+ (base+conditions).keep_with_variations (c);
+ if (c->apply && !c->variation_applied)
{
(base+substitutions).collect_feature_substitutes_with_variations (c);
- c->apply = false; // set variations only once
+ c->variation_applied = true; // set variations only once
}
}
@@ -3361,7 +3383,12 @@ struct FeatureVariations
{
c->cur_record_idx = i;
varRecords[i].collect_feature_substitutes_with_variations (c, this);
+ if (c->universal)
+ break;
}
+ if (c->variation_applied && !c->universal &&
+ !c->record_cond_idx_map->is_empty ())
+ c->insert_catch_all_feature_variation_record = true;
}
FeatureVariations* copy (hb_serialize_context_t *c) const
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh
index 8e5be92d12..e10adb78be 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh
@@ -143,9 +143,12 @@ struct hb_closure_context_t :
return active_glyphs_stack.tail ();
}
- hb_set_t& push_cur_active_glyphs ()
+ hb_set_t* push_cur_active_glyphs ()
{
- return *active_glyphs_stack.push ();
+ hb_set_t *s = active_glyphs_stack.push ();
+ if (unlikely (active_glyphs_stack.in_error ()))
+ return nullptr;
+ return s;
}
bool pop_cur_done_glyphs ()
@@ -427,6 +430,9 @@ struct hb_ot_apply_context_t :
MATCH_MAYBE
};
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
may_match_t may_match (hb_glyph_info_t &info,
hb_codepoint_t glyph_data) const
{
@@ -446,6 +452,9 @@ struct hb_ot_apply_context_t :
SKIP_MAYBE
};
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
may_skip_t may_skip (const hb_ot_apply_context_t *c,
const hb_glyph_info_t &info) const
{
@@ -516,6 +525,9 @@ struct hb_ot_apply_context_t :
}
#endif
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
void reset (unsigned int start_index_,
unsigned int num_items_)
{
@@ -525,6 +537,9 @@ struct hb_ot_apply_context_t :
matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
void reset_fast (unsigned int start_index_,
unsigned int num_items_)
{
@@ -540,6 +555,9 @@ struct hb_ot_apply_context_t :
}
matcher_t::may_skip_t
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
may_skip (const hb_glyph_info_t &info) const
{ return matcher.may_skip (c, info); }
@@ -549,6 +567,9 @@ struct hb_ot_apply_context_t :
SKIP
};
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
match_t match (hb_glyph_info_t &info)
{
matcher_t::may_skip_t skip = matcher.may_skip (c, info);
@@ -567,6 +588,9 @@ struct hb_ot_apply_context_t :
return SKIP;
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool next (unsigned *unsafe_to = nullptr)
{
assert (num_items > 0);
@@ -600,6 +624,9 @@ struct hb_ot_apply_context_t :
*unsafe_to = end;
return false;
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool prev (unsigned *unsafe_from = nullptr)
{
assert (num_items > 0);
@@ -703,6 +730,7 @@ struct hb_ot_apply_context_t :
hb_font_t *font;
hb_face_t *face;
hb_buffer_t *buffer;
+ hb_sanitize_context_t sanitizer;
recurse_func_t recurse_func = nullptr;
const GDEF &gdef;
const GDEF::accelerator_t &gdef_accel;
@@ -729,9 +757,11 @@ struct hb_ot_apply_context_t :
hb_ot_apply_context_t (unsigned int table_index_,
hb_font_t *font_,
- hb_buffer_t *buffer_) :
+ hb_buffer_t *buffer_,
+ hb_blob_t *table_blob_) :
table_index (table_index_),
font (font_), face (font->face), buffer (buffer_),
+ sanitizer (table_blob_),
gdef (
#ifndef HB_NO_OT_LAYOUT
*face->table.GDEF->table
@@ -808,6 +838,9 @@ struct hb_ot_apply_context_t :
return true;
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool check_glyph_property (const hb_glyph_info_t *info,
unsigned int match_props) const
{
@@ -1213,14 +1246,17 @@ static inline bool would_match_input (hb_would_apply_context_t *c,
return true;
}
template <typename HBUINT>
-static inline bool match_input (hb_ot_apply_context_t *c,
- unsigned int count, /* Including the first glyph (not matched) */
- const HBUINT input[], /* Array of input values--start with second glyph */
- match_func_t match_func,
- const void *match_data,
- unsigned int *end_position,
- unsigned int match_positions[HB_MAX_CONTEXT_LENGTH],
- unsigned int *p_total_component_count = nullptr)
+#ifndef HB_OPTIMIZE_SIZE
+HB_ALWAYS_INLINE
+#endif
+static bool match_input (hb_ot_apply_context_t *c,
+ unsigned int count, /* Including the first glyph (not matched) */
+ const HBUINT input[], /* Array of input values--start with second glyph */
+ match_func_t match_func,
+ const void *match_data,
+ unsigned int *end_position,
+ unsigned int match_positions[HB_MAX_CONTEXT_LENGTH],
+ unsigned int *p_total_component_count = nullptr)
{
TRACE_APPLY (nullptr);
@@ -1456,12 +1492,15 @@ static inline bool ligate_input (hb_ot_apply_context_t *c,
}
template <typename HBUINT>
-static inline bool match_backtrack (hb_ot_apply_context_t *c,
- unsigned int count,
- const HBUINT backtrack[],
- match_func_t match_func,
- const void *match_data,
- unsigned int *match_start)
+#ifndef HB_OPTIMIZE_SIZE
+HB_ALWAYS_INLINE
+#endif
+static bool match_backtrack (hb_ot_apply_context_t *c,
+ unsigned int count,
+ const HBUINT backtrack[],
+ match_func_t match_func,
+ const void *match_data,
+ unsigned int *match_start)
{
TRACE_APPLY (nullptr);
@@ -1485,13 +1524,16 @@ static inline bool match_backtrack (hb_ot_apply_context_t *c,
}
template <typename HBUINT>
-static inline bool match_lookahead (hb_ot_apply_context_t *c,
- unsigned int count,
- const HBUINT lookahead[],
- match_func_t match_func,
- const void *match_data,
- unsigned int start_index,
- unsigned int *end_index)
+#ifndef HB_OPTIMIZE_SIZE
+HB_ALWAYS_INLINE
+#endif
+static bool match_lookahead (hb_ot_apply_context_t *c,
+ unsigned int count,
+ const HBUINT lookahead[],
+ match_func_t match_func,
+ const void *match_data,
+ unsigned int start_index,
+ unsigned int *end_index)
{
TRACE_APPLY (nullptr);
@@ -1615,10 +1657,13 @@ static void context_closure_recurse_lookups (hb_closure_context_t *c,
}
covered_seq_indicies.add (seqIndex);
+ hb_set_t *cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs))
+ return;
if (has_pos_glyphs) {
- c->push_cur_active_glyphs () = std::move (pos_glyphs);
+ *cur_active_glyphs = std::move (pos_glyphs);
} else {
- c->push_cur_active_glyphs ().set (*c->glyphs);
+ *cur_active_glyphs = *c->glyphs;
}
unsigned endIndex = inputCount;
@@ -2001,8 +2046,7 @@ struct Rule
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- return_trace (inputCount.sanitize (c) &&
- lookupCount.sanitize (c) &&
+ return_trace (c->check_struct (this) &&
c->check_range (inputZ.arrayZ,
inputZ.item_size * (inputCount ? inputCount - 1 : 0) +
LookupRecord::static_size * lookupCount));
@@ -2021,6 +2065,7 @@ struct Rule
* design order */
public:
DEFINE_SIZE_ARRAY (4, inputZ);
+ DEFINE_SIZE_MAX (65536 * (Types::HBUINT::static_size + LookupRecord::static_size));
};
template <typename Types>
@@ -2168,8 +2213,9 @@ struct ContextFormat1_4
void closure (hb_closure_context_t *c) const
{
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
- get_coverage ().intersect_set (c->previous_parent_active_glyphs (), cur_active_glyphs);
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs)) return;
+ get_coverage ().intersect_set (c->previous_parent_active_glyphs (), *cur_active_glyphs);
struct ContextClosureLookupContext lookup_context = {
{intersects_glyph, intersected_glyph},
@@ -2338,9 +2384,10 @@ struct ContextFormat2_5
if (!(this+coverage).intersects (c->glyphs))
return;
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs)) return;
get_coverage ().intersect_set (c->previous_parent_active_glyphs (),
- cur_active_glyphs);
+ *cur_active_glyphs);
const ClassDef &class_def = this+classDef;
@@ -2583,10 +2630,10 @@ struct ContextFormat3
if (!(this+coverageZ[0]).intersects (c->glyphs))
return;
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs)) return;
get_coverage ().intersect_set (c->previous_parent_active_glyphs (),
- cur_active_glyphs);
-
+ *cur_active_glyphs);
const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
struct ContextClosureLookupContext lookup_context = {
@@ -2687,14 +2734,14 @@ struct ContextFormat3
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- if (!c->check_struct (this)) return_trace (false);
+ if (unlikely (!c->check_struct (this))) return_trace (false);
unsigned int count = glyphCount;
- if (!count) return_trace (false); /* We want to access coverageZ[0] freely. */
- if (!c->check_array (coverageZ.arrayZ, count)) return_trace (false);
+ if (unlikely (!count)) return_trace (false); /* We want to access coverageZ[0] freely. */
+ if (unlikely (!c->check_array (coverageZ.arrayZ, count))) return_trace (false);
for (unsigned int i = 0; i < count; i++)
- if (!coverageZ[i].sanitize (c, this)) return_trace (false);
+ if (unlikely (!coverageZ[i].sanitize (c, this))) return_trace (false);
const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
- return_trace (c->check_array (lookupRecord, lookupCount));
+ return_trace (likely (c->check_array (lookupRecord, lookupCount)));
}
protected:
@@ -3014,8 +3061,6 @@ struct ChainRule
const hb_map_t *lookahead_map = nullptr) const
{
TRACE_SERIALIZE (this);
- auto *out = c->start_embed (this);
- if (unlikely (!out)) return_trace (false);
const hb_map_t *mapping = backtrack_map;
serialize_array (c, backtrack.len, + backtrack.iter ()
@@ -3077,13 +3122,14 @@ struct ChainRule
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- if (!backtrack.sanitize (c)) return_trace (false);
+ /* Hyper-optimized sanitized because this is really hot. */
+ if (unlikely (!backtrack.len.sanitize (c))) return_trace (false);
const auto &input = StructAfter<decltype (inputX)> (backtrack);
- if (!input.sanitize (c)) return_trace (false);
+ if (unlikely (!input.lenP1.sanitize (c))) return_trace (false);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
- if (!lookahead.sanitize (c)) return_trace (false);
+ if (unlikely (!lookahead.len.sanitize (c))) return_trace (false);
const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
- return_trace (lookup.sanitize (c));
+ return_trace (likely (lookup.sanitize (c)));
}
protected:
@@ -3091,7 +3137,7 @@ struct ChainRule
backtrack; /* Array of backtracking values
* (to be matched before the input
* sequence) */
- HeadlessArrayOf<typename Types::HBUINT>
+ HeadlessArray16Of<typename Types::HBUINT>
inputX; /* Array of input values (start with
* second glyph) */
Array16Of<typename Types::HBUINT>
@@ -3102,6 +3148,7 @@ struct ChainRule
* design order) */
public:
DEFINE_SIZE_MIN (8);
+ DEFINE_SIZE_MAX (65536 * (3 * Types::HBUINT::static_size + LookupRecord::static_size));
};
template <typename Types>
@@ -3251,9 +3298,10 @@ struct ChainContextFormat1_4
void closure (hb_closure_context_t *c) const
{
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs)) return;
get_coverage ().intersect_set (c->previous_parent_active_glyphs (),
- cur_active_glyphs);
+ *cur_active_glyphs);
struct ChainContextClosureLookupContext lookup_context = {
{intersects_glyph, intersected_glyph},
@@ -3423,10 +3471,10 @@ struct ChainContextFormat2_5
if (!(this+coverage).intersects (c->glyphs))
return;
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs)) return;
get_coverage ().intersect_set (c->previous_parent_active_glyphs (),
- cur_active_glyphs);
-
+ *cur_active_glyphs);
const ClassDef &backtrack_class_def = this+backtrackClassDef;
const ClassDef &input_class_def = this+inputClassDef;
@@ -3727,10 +3775,11 @@ struct ChainContextFormat3
if (!(this+input[0]).intersects (c->glyphs))
return;
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs))
+ return;
get_coverage ().intersect_set (c->previous_parent_active_glyphs (),
- cur_active_glyphs);
-
+ *cur_active_glyphs);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
@@ -3849,8 +3898,6 @@ struct ChainContextFormat3
{
TRACE_SUBSET (this);
- auto *out = c->serializer->start_embed (this);
- if (unlikely (!out)) return_trace (false);
if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
if (!serialize_coverage_offsets (c, backtrack.iter (), this))
@@ -3877,14 +3924,14 @@ struct ChainContextFormat3
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- if (!backtrack.sanitize (c, this)) return_trace (false);
+ if (unlikely (!backtrack.sanitize (c, this))) return_trace (false);
const auto &input = StructAfter<decltype (inputX)> (backtrack);
- if (!input.sanitize (c, this)) return_trace (false);
- if (!input.len) return_trace (false); /* To be consistent with Context. */
+ if (unlikely (!input.sanitize (c, this))) return_trace (false);
+ if (unlikely (!input.len)) return_trace (false); /* To be consistent with Context. */
const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
- if (!lookahead.sanitize (c, this)) return_trace (false);
+ if (unlikely (!lookahead.sanitize (c, this))) return_trace (false);
const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
- return_trace (lookup.sanitize (c));
+ return_trace (likely (lookup.sanitize (c)));
}
protected:
@@ -3974,7 +4021,7 @@ struct ExtensionFormat1
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->format = format;
out->extensionLookupType = extensionLookupType;
@@ -4503,7 +4550,10 @@ struct GSUBGPOS
{
accelerator_t (hb_face_t *face)
{
- this->table = hb_sanitize_context_t ().reference_table<T> (face);
+ hb_sanitize_context_t sc;
+ sc.lazy_some_gpos = true;
+ this->table = sc.reference_table<T> (face);
+
if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face)))
{
hb_blob_destroy (this->table.get_blob ());
@@ -4528,6 +4578,8 @@ struct GSUBGPOS
this->table.destroy ();
}
+ hb_blob_t *get_blob () const { return table.get_blob (); }
+
hb_ot_layout_lookup_accelerator_t *get_accel (unsigned lookup_index) const
{
if (unlikely (lookup_index >= lookup_count)) return nullptr;
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout.cc b/thirdparty/harfbuzz/src/hb-ot-layout.cc
index c66ee8cfd0..020b8a6c82 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-layout.cc
@@ -1316,8 +1316,7 @@ hb_ot_layout_collect_lookups (hb_face_t *face,
hb_set_t feature_indexes;
hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes);
- for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID;
- hb_set_next (&feature_indexes, &feature_index);)
+ for (auto feature_index : feature_indexes)
g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes);
g.feature_variation_collect_lookups (&feature_indexes, nullptr, lookup_indexes);
@@ -1570,7 +1569,7 @@ hb_ot_layout_lookups_substitute_closure (hb_face_t *face,
glyphs_length = glyphs->get_population ();
if (lookups)
{
- for (hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID; hb_set_next (lookups, &lookup_index);)
+ for (auto lookup_index : *lookups)
gsub.get_lookup (lookup_index).closure (&c, lookup_index);
}
else
@@ -1953,7 +1952,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
{
const unsigned int table_index = proxy.table_index;
unsigned int i = 0;
- OT::hb_ot_apply_context_t c (table_index, font, buffer);
+ OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob ());
c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>);
for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++)
@@ -2011,20 +2010,20 @@ void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, h
{
GSUBProxy proxy (font->face);
if (buffer->messaging () &&
- !buffer->message (font, "start table GSUB")) return;
+ !buffer->message (font, "start table GSUB script tag '%c%c%c%c'", HB_UNTAG (chosen_script[0]))) return;
apply (proxy, plan, font, buffer);
if (buffer->messaging ())
- (void) buffer->message (font, "end table GSUB");
+ (void) buffer->message (font, "end table GSUB script tag '%c%c%c%c'", HB_UNTAG (chosen_script[0]));
}
void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
{
GPOSProxy proxy (font->face);
if (buffer->messaging () &&
- !buffer->message (font, "start table GPOS")) return;
+ !buffer->message (font, "start table GPOS script tag '%c%c%c%c'", HB_UNTAG (chosen_script[1]))) return;
apply (proxy, plan, font, buffer);
if (buffer->messaging ())
- (void) buffer->message (font, "end table GPOS");
+ (void) buffer->message (font, "end table GPOS script tag '%c%c%c%c'", HB_UNTAG (chosen_script[1]));
}
void
@@ -2036,6 +2035,112 @@ hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c,
}
#ifndef HB_NO_BASE
+
+static void
+choose_base_tags (hb_script_t script,
+ hb_language_t language,
+ hb_tag_t *script_tag,
+ hb_tag_t *language_tag)
+{
+ hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
+ unsigned script_count = ARRAY_LENGTH (script_tags);
+
+ hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
+ unsigned language_count = ARRAY_LENGTH (language_tags);
+
+ hb_ot_tags_from_script_and_language (script, language,
+ &script_count, script_tags,
+ &language_count, language_tags);
+
+ *script_tag = script_count ? script_tags[script_count - 1] : HB_OT_TAG_DEFAULT_SCRIPT;
+ *language_tag = language_count ? language_tags[language_count - 1] : HB_OT_TAG_DEFAULT_LANGUAGE;
+}
+
+/**
+ * hb_ot_layout_get_font_extents:
+ * @font: a font
+ * @direction: text direction.
+ * @script_tag: script tag.
+ * @language_tag: language tag.
+ * @extents: (out) (nullable): font extents if found.
+ *
+ * Fetches script/language-specific font extents. These values are
+ * looked up in the `BASE` table's `MinMax` records.
+ *
+ * If no such extents are found, the default extents for the font are
+ * fetched. As such, the return value of this function can for the
+ * most part be ignored. Note that the per-script/language extents
+ * do not have a line-gap value, and the line-gap is set to zero in
+ * that case.
+ *
+ * Return value: `true` if found script/language-specific font extents.
+ *
+ * Since: 8.0.0
+ **/
+hb_bool_t
+hb_ot_layout_get_font_extents (hb_font_t *font,
+ hb_direction_t direction,
+ hb_tag_t script_tag,
+ hb_tag_t language_tag,
+ hb_font_extents_t *extents)
+{
+ hb_position_t min, max;
+ if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE,
+ &min, &max))
+ {
+ if (extents)
+ {
+ extents->ascender = max;
+ extents->descender = min;
+ extents->line_gap = 0;
+ }
+ return true;
+ }
+
+ hb_font_get_extents_for_direction (font, direction, extents);
+ return false;
+}
+
+/**
+ * hb_ot_layout_get_font_extents2:
+ * @font: a font
+ * @direction: text direction.
+ * @script: script.
+ * @language: (nullable): language.
+ * @extents: (out) (nullable): font extents if found.
+ *
+ * Fetches script/language-specific font extents. These values are
+ * looked up in the `BASE` table's `MinMax` records.
+ *
+ * If no such extents are found, the default extents for the font are
+ * fetched. As such, the return value of this function can for the
+ * most part be ignored. Note that the per-script/language extents
+ * do not have a line-gap value, and the line-gap is set to zero in
+ * that case.
+ *
+ * This function is like hb_ot_layout_get_font_extents() but takes
+ * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
+ *
+ * Return value: `true` if found script/language-specific font extents.
+ *
+ * Since: 8.0.0
+ **/
+hb_bool_t
+hb_ot_layout_get_font_extents2 (hb_font_t *font,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_font_extents_t *extents)
+{
+ hb_tag_t script_tag, language_tag;
+ choose_base_tags (script, language, &script_tag, &language_tag);
+ return hb_ot_layout_get_font_extents (font,
+ direction,
+ script_tag,
+ language_tag,
+ extents);
+}
+
/**
* hb_ot_layout_get_horizontal_baseline_tag_for_script:
* @script: a script tag.
@@ -2134,6 +2239,42 @@ hb_ot_layout_get_baseline (hb_font_t *font,
}
/**
+ * hb_ot_layout_get_baseline2:
+ * @font: a font
+ * @baseline_tag: a baseline tag
+ * @direction: text direction.
+ * @script: script.
+ * @language: (nullable): language, currently unused.
+ * @coord: (out) (nullable): baseline value if found.
+ *
+ * Fetches a baseline value from the face.
+ *
+ * This function is like hb_ot_layout_get_baseline() but takes
+ * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
+ *
+ * Return value: `true` if found baseline value in the font.
+ *
+ * Since: 8.0.0
+ **/
+hb_bool_t
+hb_ot_layout_get_baseline2 (hb_font_t *font,
+ hb_ot_layout_baseline_tag_t baseline_tag,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_position_t *coord /* OUT. May be NULL. */)
+{
+ hb_tag_t script_tag, language_tag;
+ choose_base_tags (script, language, &script_tag, &language_tag);
+ return hb_ot_layout_get_baseline (font,
+ baseline_tag,
+ direction,
+ script_tag,
+ language_tag,
+ coord);
+}
+
+/**
* hb_ot_layout_get_baseline_with_fallback:
* @font: a font
* @baseline_tag: a baseline tag
@@ -2355,6 +2496,41 @@ hb_ot_layout_get_baseline_with_fallback (hb_font_t *font,
}
}
+/**
+ * hb_ot_layout_get_baseline_with_fallback2:
+ * @font: a font
+ * @baseline_tag: a baseline tag
+ * @direction: text direction.
+ * @script: script.
+ * @language: (nullable): language, currently unused.
+ * @coord: (out): baseline value if found.
+ *
+ * Fetches a baseline value from the face, and synthesizes
+ * it if the font does not have it.
+ *
+ * This function is like hb_ot_layout_get_baseline_with_fallback() but takes
+ * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
+ *
+ * Since: 8.0.0
+ **/
+void
+hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font,
+ hb_ot_layout_baseline_tag_t baseline_tag,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_position_t *coord /* OUT */)
+{
+ hb_tag_t script_tag, language_tag;
+ choose_base_tags (script, language, &script_tag, &language_tag);
+ hb_ot_layout_get_baseline_with_fallback (font,
+ baseline_tag,
+ direction,
+ script_tag,
+ language_tag,
+ coord);
+}
+
#endif
@@ -2451,9 +2627,10 @@ hb_ot_layout_lookup_get_optical_bound (hb_font_t *font,
hb_codepoint_t glyph)
{
const OT::PosLookup &lookup = font->face->table.GPOS->table->get_lookup (lookup_index);
+ hb_blob_t *blob = font->face->table.GPOS->get_blob ();
hb_glyph_position_t pos = {0};
hb_position_single_dispatch_t c;
- lookup.dispatch (&c, font, direction, glyph, pos);
+ lookup.dispatch (&c, font, blob, direction, glyph, pos);
hb_position_t ret = 0;
switch (direction)
{
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout.h b/thirdparty/harfbuzz/src/hb-ot-layout.h
index 10dcc65ac0..b0fae3707f 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout.h
+++ b/thirdparty/harfbuzz/src/hb-ot-layout.h
@@ -447,6 +447,20 @@ hb_ot_layout_feature_get_characters (hb_face_t *face,
* BASE
*/
+HB_EXTERN hb_bool_t
+hb_ot_layout_get_font_extents (hb_font_t *font,
+ hb_direction_t direction,
+ hb_tag_t script_tag,
+ hb_tag_t language_tag,
+ hb_font_extents_t *extents);
+
+HB_EXTERN hb_bool_t
+hb_ot_layout_get_font_extents2 (hb_font_t *font,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_font_extents_t *extents);
+
/**
* hb_ot_layout_baseline_tag_t:
* @HB_OT_LAYOUT_BASELINE_TAG_ROMAN: The baseline used by alphabetic scripts such as Latin, Cyrillic and Greek.
@@ -499,6 +513,14 @@ hb_ot_layout_get_baseline (hb_font_t *font,
hb_tag_t language_tag,
hb_position_t *coord /* OUT. May be NULL. */);
+HB_EXTERN hb_bool_t
+hb_ot_layout_get_baseline2 (hb_font_t *font,
+ hb_ot_layout_baseline_tag_t baseline_tag,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_position_t *coord /* OUT. May be NULL. */);
+
HB_EXTERN void
hb_ot_layout_get_baseline_with_fallback (hb_font_t *font,
hb_ot_layout_baseline_tag_t baseline_tag,
@@ -507,6 +529,14 @@ hb_ot_layout_get_baseline_with_fallback (hb_font_t *font,
hb_tag_t language_tag,
hb_position_t *coord /* OUT */);
+HB_EXTERN void
+hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font,
+ hb_ot_layout_baseline_tag_t baseline_tag,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_position_t *coord /* OUT */);
+
HB_END_DECLS
#endif /* HB_OT_LAYOUT_H */
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout.hh b/thirdparty/harfbuzz/src/hb-ot-layout.hh
index 9505d5f147..d71889331d 100644
--- a/thirdparty/harfbuzz/src/hb-ot-layout.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-layout.hh
@@ -448,7 +448,7 @@ _hb_glyph_info_get_lig_id (const hb_glyph_info_t *info)
static inline bool
_hb_glyph_info_ligated_internal (const hb_glyph_info_t *info)
{
- return !!(info->lig_props() & IS_LIG_BASE);
+ return info->lig_props() & IS_LIG_BASE;
}
static inline unsigned int
@@ -496,37 +496,37 @@ _hb_glyph_info_get_glyph_props (const hb_glyph_info_t *info)
static inline bool
_hb_glyph_info_is_base_glyph (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH;
}
static inline bool
_hb_glyph_info_is_ligature (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE;
}
static inline bool
_hb_glyph_info_is_mark (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK;
}
static inline bool
_hb_glyph_info_substituted (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED;
}
static inline bool
_hb_glyph_info_ligated (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATED);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATED;
}
static inline bool
_hb_glyph_info_multiplied (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
}
static inline bool
diff --git a/thirdparty/harfbuzz/src/hb-ot-map.cc b/thirdparty/harfbuzz/src/hb-ot-map.cc
index 8882dbaccb..bacd56ef3f 100644
--- a/thirdparty/harfbuzz/src/hb-ot-map.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-map.cc
@@ -213,7 +213,8 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m,
/* Sort features and merge duplicates */
if (feature_infos.length)
{
- feature_infos.qsort ();
+ if (!is_simple)
+ feature_infos.qsort ();
auto *f = feature_infos.arrayZ;
unsigned int j = 0;
unsigned count = feature_infos.length;
@@ -314,7 +315,8 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m,
map->needs_fallback = !found;
}
//feature_infos.shrink (0); /* Done with these */
-
+ if (is_simple)
+ m.features.qsort ();
add_gsub_pause (nullptr);
add_gpos_pause (nullptr);
@@ -350,7 +352,7 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m,
}
/* Sort lookups and merge duplicates */
- if (last_num_lookups < lookups.length)
+ if (last_num_lookups + 1 < lookups.length)
{
lookups.as_array ().sub_array (last_num_lookups, lookups.length - last_num_lookups).qsort ();
diff --git a/thirdparty/harfbuzz/src/hb-ot-map.hh b/thirdparty/harfbuzz/src/hb-ot-map.hh
index efc8cae96a..8af8129ceb 100644
--- a/thirdparty/harfbuzz/src/hb-ot-map.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-map.hh
@@ -60,6 +60,13 @@ struct hb_ot_map_t
int cmp (const hb_tag_t tag_) const
{ return tag_ < tag ? -1 : tag_ > tag ? 1 : 0; }
+
+ HB_INTERNAL static int cmp (const void *pa, const void *pb)
+ {
+ const feature_map_t *a = (const feature_map_t *) pa;
+ const feature_map_t *b = (const feature_map_t *) pb;
+ return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0;
+ }
};
struct lookup_map_t {
@@ -273,6 +280,7 @@ struct hb_ot_map_builder_t
hb_face_t *face;
hb_segment_properties_t props;
+ bool is_simple;
hb_tag_t chosen_script[2];
bool found_script[2];
diff --git a/thirdparty/harfbuzz/src/hb-ot-math-table.hh b/thirdparty/harfbuzz/src/hb-ot-math-table.hh
index dccf720f46..b11da464b2 100644
--- a/thirdparty/harfbuzz/src/hb-ot-math-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-math-table.hh
@@ -73,7 +73,6 @@ struct MathConstants
{
TRACE_SERIALIZE (this);
auto *out = c->start_embed (this);
- if (unlikely (!out)) return_trace (nullptr);
HBINT16 *p = c->allocate_size<HBINT16> (HBINT16::static_size * 2);
if (unlikely (!p)) return_trace (nullptr);
@@ -310,7 +309,6 @@ struct MathKern
{
TRACE_SERIALIZE (this);
auto *out = c->start_embed (this);
- if (unlikely (!out)) return_trace (nullptr);
if (unlikely (!c->embed (heightCount))) return_trace (nullptr);
@@ -572,6 +570,7 @@ struct MathGlyphInfo
auto it =
+ hb_iter (this+extendedShapeCoverage)
+ | hb_take (c->plan->source->get_num_glyphs ())
| hb_filter (glyphset)
| hb_map_retains_sorting (glyph_map)
;
@@ -757,8 +756,6 @@ struct MathGlyphAssembly
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
- auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out)) return_trace (false);
if (!c->serializer->copy (italicsCorrection, this)) return_trace (false);
if (!c->serializer->copy<HBUINT16> (partRecords.len)) return_trace (false);
@@ -945,13 +942,13 @@ struct MathVariants
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
if (!c->serializer->check_assign (out->minConnectorOverlap, minConnectorOverlap, HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
-
+
hb_sorted_vector_t<hb_codepoint_t> new_vert_coverage;
hb_sorted_vector_t<hb_codepoint_t> new_hori_coverage;
hb_set_t indices;
collect_coverage_and_indices (new_vert_coverage, vertGlyphCoverage, 0, vertGlyphCount, indices, glyphset, glyph_map);
collect_coverage_and_indices (new_hori_coverage, horizGlyphCoverage, vertGlyphCount, vertGlyphCount + horizGlyphCount, indices, glyphset, glyph_map);
-
+
if (!c->serializer->check_assign (out->vertGlyphCount, new_vert_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
if (!c->serializer->check_assign (out->horizGlyphCount, new_hori_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
@@ -963,10 +960,10 @@ struct MathVariants
if (!o) return_trace (false);
o->serialize_subset (c, glyphConstruction[i], this);
}
-
+
if (new_vert_coverage)
out->vertGlyphCoverage.serialize_serialize (c->serializer, new_vert_coverage.iter ());
-
+
if (new_hori_coverage)
out->horizGlyphCoverage.serialize_serialize (c->serializer, new_hori_coverage.iter ());
return_trace (true);
diff --git a/thirdparty/harfbuzz/src/hb-ot-os2-table.hh b/thirdparty/harfbuzz/src/hb-ot-os2-table.hh
index 97d18b9d75..72cb662473 100644
--- a/thirdparty/harfbuzz/src/hb-ot-os2-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-os2-table.hh
@@ -249,7 +249,7 @@ struct OS2
if (c->plan->user_axes_location.has (HB_TAG ('w','g','h','t')) &&
!c->plan->pinned_at_default)
{
- float weight_class = c->plan->user_axes_location.get (HB_TAG ('w','g','h','t'));
+ float weight_class = c->plan->user_axes_location.get (HB_TAG ('w','g','h','t')).middle;
if (!c->serializer->check_assign (os2_prime->usWeightClass,
roundf (hb_clamp (weight_class, 1.0f, 1000.0f)),
HB_SERIALIZE_ERROR_INT_OVERFLOW))
@@ -259,7 +259,7 @@ struct OS2
if (c->plan->user_axes_location.has (HB_TAG ('w','d','t','h')) &&
!c->plan->pinned_at_default)
{
- float width = c->plan->user_axes_location.get (HB_TAG ('w','d','t','h'));
+ float width = c->plan->user_axes_location.get (HB_TAG ('w','d','t','h')).middle;
if (!c->serializer->check_assign (os2_prime->usWidthClass,
roundf (map_wdth_to_widthclass (width)),
HB_SERIALIZE_ERROR_INT_OVERFLOW))
@@ -287,8 +287,7 @@ struct OS2
/* This block doesn't show up in profiles. If it ever did,
* we can rewrite it to iterate over OS/2 ranges and use
* set iteration to check if the range matches. */
- for (hb_codepoint_t cp = HB_SET_VALUE_INVALID;
- codepoints->next (&cp);)
+ for (auto cp : *codepoints)
{
unsigned int bit = _hb_ot_os2_get_unicode_range_bit (cp);
if (bit < 128)
diff --git a/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh b/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh
index 951e6395d6..d44233610a 100644
--- a/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-post-table-v2subset.hh
@@ -79,6 +79,11 @@ HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const
post::accelerator_t _post (c->plan->source);
hb_hashmap_t<hb_bytes_t, uint32_t, true> glyph_name_to_new_index;
+
+ old_new_index_map.alloc (num_glyphs);
+ old_gid_new_index_map.alloc (num_glyphs);
+ glyph_name_to_new_index.alloc (num_glyphs);
+
for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++)
{
hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid);
@@ -86,11 +91,12 @@ HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const
unsigned new_index;
const uint32_t *new_index2;
- if (old_index <= 257) new_index = old_index;
+ if (old_index <= 257)
+ new_index = old_index;
else if (old_new_index_map.has (old_index, &new_index2))
- {
new_index = *new_index2;
- } else {
+ else
+ {
hb_bytes_t s = _post.find_glyph_name (old_gid);
new_index = glyph_name_to_new_index.get (s);
if (new_index == (unsigned)-1)
diff --git a/thirdparty/harfbuzz/src/hb-ot-post-table.hh b/thirdparty/harfbuzz/src/hb-ot-post-table.hh
index 042fa611ad..761e49d119 100644
--- a/thirdparty/harfbuzz/src/hb-ot-post-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-post-table.hh
@@ -96,8 +96,7 @@ struct post
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
- post *post_prime = c->serializer->start_embed<post> ();
- if (unlikely (!post_prime)) return_trace (false);
+ auto *post_prime = c->serializer->start_embed<post> ();
bool glyph_names = c->plan->flags & HB_SUBSET_FLAGS_GLYPH_NAMES;
if (!serialize (c->serializer, glyph_names))
@@ -117,7 +116,7 @@ struct post
if (c->plan->user_axes_location.has (HB_TAG ('s','l','n','t')) &&
!c->plan->pinned_at_default)
{
- float italic_angle = c->plan->user_axes_location.get (HB_TAG ('s','l','n','t'));
+ float italic_angle = c->plan->user_axes_location.get (HB_TAG ('s','l','n','t')).middle;
italic_angle = hb_max (-90.f, hb_min (italic_angle, 90.f));
post_prime->italicAngle.set_float (italic_angle);
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape.cc b/thirdparty/harfbuzz/src/hb-ot-shape.cc
index 3d207e0681..d84313f190 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shape.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shape.cc
@@ -313,6 +313,8 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
{
hb_ot_map_builder_t *map = &planner->map;
+ map->is_simple = true;
+
map->enable_feature (HB_TAG('r','v','r','n'));
map->add_gsub_pause (nullptr);
@@ -354,7 +356,10 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */
if (planner->shaper->collect_features)
+ {
+ map->is_simple = false;
planner->shaper->collect_features (planner);
+ }
map->enable_feature (HB_TAG ('B','u','z','z')); /* Considered required. */
map->enable_feature (HB_TAG ('B','U','Z','Z')); /* Considered discretionary. */
@@ -378,6 +383,8 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH);
}
+ if (num_user_features)
+ map->is_simple = false;
for (unsigned int i = 0; i < num_user_features; i++)
{
const hb_feature_t *feature = &user_features[i];
diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh
index e7a69008b7..66a8bfbd28 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh
@@ -368,7 +368,7 @@ arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan,
hb_font_t *font,
hb_buffer_t *buffer)
{
- OT::hb_ot_apply_context_t c (0, font, buffer);
+ OT::hb_ot_apply_context_t c (0, font, buffer, hb_blob_get_empty ());
for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
if (fallback_plan->lookup_array[i]) {
c.set_lookup_mask (fallback_plan->mask_array[i]);
diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh
index 353e32d32c..7dd47755af 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh
@@ -53,7 +53,7 @@ enum indic_syllable_type_t {
};
-#line 57 "hb-ot-shaper-indic-machine.hh"
+#line 54 "hb-ot-shaper-indic-machine.hh"
#define indic_syllable_machine_ex_A 9u
#define indic_syllable_machine_ex_C 1u
#define indic_syllable_machine_ex_CM 16u
@@ -76,7 +76,7 @@ enum indic_syllable_type_t {
#define indic_syllable_machine_ex_ZWNJ 5u
-#line 80 "hb-ot-shaper-indic-machine.hh"
+#line 75 "hb-ot-shaper-indic-machine.hh"
static const unsigned char _indic_syllable_machine_trans_keys[] = {
8u, 8u, 4u, 13u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u,
8u, 8u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, 4u, 13u,
@@ -460,7 +460,7 @@ find_syllables_indic (hb_buffer_t *buffer)
int cs;
hb_glyph_info_t *info = buffer->info;
-#line 464 "hb-ot-shaper-indic-machine.hh"
+#line 453 "hb-ot-shaper-indic-machine.hh"
{
cs = indic_syllable_machine_start;
ts = 0;
@@ -476,7 +476,7 @@ find_syllables_indic (hb_buffer_t *buffer)
unsigned int syllable_serial = 1;
-#line 480 "hb-ot-shaper-indic-machine.hh"
+#line 465 "hb-ot-shaper-indic-machine.hh"
{
int _slen;
int _trans;
@@ -490,7 +490,7 @@ _resume:
#line 1 "NONE"
{ts = p;}
break;
-#line 494 "hb-ot-shaper-indic-machine.hh"
+#line 477 "hb-ot-shaper-indic-machine.hh"
}
_keys = _indic_syllable_machine_trans_keys + (cs<<1);
@@ -593,7 +593,7 @@ _eof_trans:
#line 114 "hb-ot-shaper-indic-machine.rl"
{act = 6;}
break;
-#line 597 "hb-ot-shaper-indic-machine.hh"
+#line 559 "hb-ot-shaper-indic-machine.hh"
}
_again:
@@ -602,7 +602,7 @@ _again:
#line 1 "NONE"
{ts = 0;}
break;
-#line 606 "hb-ot-shaper-indic-machine.hh"
+#line 566 "hb-ot-shaper-indic-machine.hh"
}
if ( ++p != pe )
diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.cc
index 89226ae4a1..97f62035c6 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shaper-syllabic.cc
@@ -40,6 +40,14 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font,
if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
return false;
if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE)))
+ {
+ if (buffer->messaging ())
+ (void) buffer->message (font, "skipped inserting dotted-circles because there is no broken syllables");
+ return false;
+ }
+
+ if (buffer->messaging () &&
+ !buffer->message (font, "start inserting dotted-circles"))
return false;
hb_codepoint_t dottedcircle_glyph;
@@ -84,6 +92,10 @@ hb_syllabic_insert_dotted_circles (hb_font_t *font,
(void) buffer->next_glyph ();
}
buffer->sync ();
+
+ if (buffer->messaging ())
+ (void) buffer->message (font, "end inserting dotted-circles");
+
return true;
}
diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-use.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-use.cc
index 342aba1235..c35765af95 100644
--- a/thirdparty/harfbuzz/src/hb-ot-shaper-use.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-shaper-use.cc
@@ -377,6 +377,9 @@ reorder_syllable_use (hb_buffer_t *buffer, unsigned int start, unsigned int end)
#define POST_BASE_FLAGS64 (FLAG64 (USE(FAbv)) | \
FLAG64 (USE(FBlw)) | \
FLAG64 (USE(FPst)) | \
+ FLAG64 (USE(FMAbv)) | \
+ FLAG64 (USE(FMBlw)) | \
+ FLAG64 (USE(FMPst)) | \
FLAG64 (USE(MAbv)) | \
FLAG64 (USE(MBlw)) | \
FLAG64 (USE(MPst)) | \
diff --git a/thirdparty/harfbuzz/src/hb-ot-stat-table.hh b/thirdparty/harfbuzz/src/hb-ot-stat-table.hh
index de553dd72f..f7bb3791ca 100644
--- a/thirdparty/harfbuzz/src/hb-ot-stat-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-stat-table.hh
@@ -57,6 +57,16 @@ enum
// Reserved = 0xFFFC /* Reserved for future use — set to zero. */
};
+static bool axis_value_is_outside_axis_range (hb_tag_t axis_tag, float axis_value,
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location)
+{
+ if (!user_axes_location->has (axis_tag))
+ return false;
+
+ Triple axis_range = user_axes_location->get (axis_tag);
+ return (axis_value < axis_range.minimum || axis_value > axis_range.maximum);
+}
+
struct StatAxisRecord
{
int cmp (hb_tag_t key) const { return tag.cmp (key); }
@@ -96,23 +106,19 @@ struct AxisValueFormat1
}
bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
- const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
{
hb_tag_t axis_tag = get_axis_tag (axis_records);
float axis_value = get_value ();
- if (!user_axes_location->has (axis_tag) ||
- fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f)
- return true;
-
- return false;
+ return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
}
bool subset (hb_subset_context_t *c,
const hb_array_t<const StatAxisRecord> axis_records) const
{
TRACE_SUBSET (this);
- const hb_hashmap_t<hb_tag_t, float>* user_axes_location = &c->plan->user_axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
if (keep_axis_value (axis_records, user_axes_location))
return_trace (c->serializer->embed (this));
@@ -155,23 +161,19 @@ struct AxisValueFormat2
}
bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
- const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
{
hb_tag_t axis_tag = get_axis_tag (axis_records);
float axis_value = get_value ();
- if (!user_axes_location->has (axis_tag) ||
- fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f)
- return true;
-
- return false;
+ return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
}
bool subset (hb_subset_context_t *c,
const hb_array_t<const StatAxisRecord> axis_records) const
{
TRACE_SUBSET (this);
- const hb_hashmap_t<hb_tag_t, float>* user_axes_location = &c->plan->user_axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
if (keep_axis_value (axis_records, user_axes_location))
return_trace (c->serializer->embed (this));
@@ -218,23 +220,19 @@ struct AxisValueFormat3
}
bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
- const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
{
hb_tag_t axis_tag = get_axis_tag (axis_records);
float axis_value = get_value ();
- if (!user_axes_location->has (axis_tag) ||
- fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f)
- return true;
-
- return false;
+ return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
}
bool subset (hb_subset_context_t *c,
const hb_array_t<const StatAxisRecord> axis_records) const
{
TRACE_SUBSET (this);
- const hb_hashmap_t<hb_tag_t, float>* user_axes_location = &c->plan->user_axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
if (keep_axis_value (axis_records, user_axes_location))
return_trace (c->serializer->embed (this));
@@ -291,7 +289,7 @@ struct AxisValueFormat4
{ return axisValues.as_array (axisCount)[axis_index]; }
bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
- const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
{
hb_array_t<const AxisValueRecord> axis_value_records = axisValues.as_array (axisCount);
@@ -301,8 +299,7 @@ struct AxisValueFormat4
float axis_value = rec.get_value ();
hb_tag_t axis_tag = axis_records[axis_idx].get_axis_tag ();
- if (user_axes_location->has (axis_tag) &&
- fabsf(axis_value - user_axes_location->get (axis_tag)) > 0.001f)
+ if (axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location))
return false;
}
@@ -313,7 +310,7 @@ struct AxisValueFormat4
const hb_array_t<const StatAxisRecord> axis_records) const
{
TRACE_SUBSET (this);
- const hb_hashmap_t<hb_tag_t, float> *user_axes_location = &c->plan->user_axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location = &c->plan->user_axes_location;
if (!keep_axis_value (axis_records, user_axes_location))
return_trace (false);
@@ -402,7 +399,7 @@ struct AxisValue
}
bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
- hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
+ hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
{
switch (u.format)
{
@@ -451,8 +448,6 @@ struct AxisValueOffsetArray: UnsizedArrayOf<Offset16To<AxisValue>>
const hb_array_t<const StatAxisRecord> axis_records) const
{
TRACE_SUBSET (this);
- auto *out = c->serializer->start_embed (this);
- if (unlikely (!out)) return_trace (false);
auto axisValueOffsets = as_array (axisValueCount);
count = 0;
@@ -517,7 +512,7 @@ struct STAT
return axis_value.get_value_name_id ();
}
- void collect_name_ids (hb_hashmap_t<hb_tag_t, float> *user_axes_location,
+ void collect_name_ids (hb_hashmap_t<hb_tag_t, Triple> *user_axes_location,
hb_set_t *nameids_to_retain /* OUT */) const
{
if (!has_data ()) return;
diff --git a/thirdparty/harfbuzz/src/hb-ot-tag.cc b/thirdparty/harfbuzz/src/hb-ot-tag.cc
index 547f9573d0..53b6b38f66 100644
--- a/thirdparty/harfbuzz/src/hb-ot-tag.cc
+++ b/thirdparty/harfbuzz/src/hb-ot-tag.cc
@@ -412,7 +412,7 @@ parse_private_use_subtag (const char *private_use_subtag,
/**
* hb_ot_tags_from_script_and_language:
* @script: an #hb_script_t to convert.
- * @language: an #hb_language_t to convert.
+ * @language: (nullable): an #hb_language_t to convert.
* @script_count: (inout) (optional): maximum number of script tags to retrieve (IN)
* and actual number of script tags retrieved (OUT)
* @script_tags: (out) (optional): array of size at least @script_count to store the
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-common.hh b/thirdparty/harfbuzz/src/hb-ot-var-common.hh
index 7d4bf2241c..44ec64bc03 100644
--- a/thirdparty/harfbuzz/src/hb-ot-var-common.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-var-common.hh
@@ -36,19 +36,14 @@ struct DeltaSetIndexMapFormat01
{
friend struct DeltaSetIndexMap;
+ unsigned get_size () const
+ { return min_size + mapCount * get_width (); }
+
private:
DeltaSetIndexMapFormat01* copy (hb_serialize_context_t *c) const
{
TRACE_SERIALIZE (this);
- auto *out = c->start_embed (this);
- if (unlikely (!out)) return_trace (nullptr);
-
- unsigned total_size = min_size + mapCount * get_width ();
- HBUINT8 *p = c->allocate_size<HBUINT8> (total_size);
- if (unlikely (!p)) return_trace (nullptr);
-
- hb_memcpy (p, this, HBUINT8::static_size * total_size);
- return_trace (out);
+ return_trace (c->embed (this));
}
template <typename T>
@@ -65,7 +60,7 @@ struct DeltaSetIndexMapFormat01
entryFormat = ((width-1)<<4)|(inner_bit_count-1);
mapCount = output_map.length;
- HBUINT8 *p = c->allocate_size<HBUINT8> (width * output_map.length);
+ HBUINT8 *p = c->allocate_size<HBUINT8> (width * output_map.length, false);
if (unlikely (!p)) return_trace (false);
for (unsigned int i = 0; i < output_map.length; i++)
{
@@ -242,6 +237,7 @@ struct VarStoreInstancer
/* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */
struct TupleVariationHeader
{
+ friend struct tuple_delta_t;
unsigned get_size (unsigned axis_count) const
{ return min_size + get_all_tuples (axis_count).get_size (); }
@@ -250,14 +246,67 @@ struct TupleVariationHeader
const TupleVariationHeader &get_next (unsigned axis_count) const
{ return StructAtOffset<TupleVariationHeader> (this, get_size (axis_count)); }
+ bool unpack_axis_tuples (unsigned axis_count,
+ const hb_array_t<const F2DOT14> shared_tuples,
+ const hb_map_t *axes_old_index_tag_map,
+ hb_hashmap_t<hb_tag_t, Triple>& axis_tuples /* OUT */) const
+ {
+ const F2DOT14 *peak_tuple = nullptr;
+ if (has_peak ())
+ peak_tuple = get_peak_tuple (axis_count).arrayZ;
+ else
+ {
+ unsigned int index = get_index ();
+ if (unlikely ((index + 1) * axis_count > shared_tuples.length))
+ return false;
+ peak_tuple = shared_tuples.sub_array (axis_count * index, axis_count).arrayZ;
+ }
+
+ const F2DOT14 *start_tuple = nullptr;
+ const F2DOT14 *end_tuple = nullptr;
+ bool has_interm = has_intermediate ();
+
+ if (has_interm)
+ {
+ start_tuple = get_start_tuple (axis_count).arrayZ;
+ end_tuple = get_end_tuple (axis_count).arrayZ;
+ }
+
+ for (unsigned i = 0; i < axis_count; i++)
+ {
+ float peak = peak_tuple[i].to_float ();
+ if (peak == 0.f) continue;
+
+ hb_tag_t *axis_tag;
+ if (!axes_old_index_tag_map->has (i, &axis_tag))
+ return false;
+
+ float start, end;
+ if (has_interm)
+ {
+ start = start_tuple[i].to_float ();
+ end = end_tuple[i].to_float ();
+ }
+ else
+ {
+ start = hb_min (peak, 0.f);
+ end = hb_max (peak, 0.f);
+ }
+ axis_tuples.set (*axis_tag, Triple (start, peak, end));
+ }
+
+ return true;
+ }
+
float calculate_scalar (hb_array_t<int> coords, unsigned int coord_count,
const hb_array_t<const F2DOT14> shared_tuples,
- const hb_vector_t<int> *shared_tuple_active_idx = nullptr) const
+ const hb_vector_t<hb_pair_t<int,int>> *shared_tuple_active_idx = nullptr) const
{
const F2DOT14 *peak_tuple;
unsigned start_idx = 0;
unsigned end_idx = coord_count;
+ unsigned step = 1;
if (has_peak ())
peak_tuple = get_peak_tuple (coord_count).arrayZ;
@@ -272,10 +321,16 @@ struct TupleVariationHeader
{
if (unlikely (index >= shared_tuple_active_idx->length))
return 0.f;
- int v = (*shared_tuple_active_idx).arrayZ[index];
- if (v != -1)
+ auto _ = (*shared_tuple_active_idx).arrayZ[index];
+ if (_.second != -1)
+ {
+ start_idx = _.first;
+ end_idx = _.second + 1;
+ step = _.second - _.first;
+ }
+ else if (_.first != -1)
{
- start_idx = v;
+ start_idx = _.first;
end_idx = start_idx + 1;
}
}
@@ -291,7 +346,7 @@ struct TupleVariationHeader
}
float scalar = 1.f;
- for (unsigned int i = start_idx; i < end_idx; i++)
+ for (unsigned int i = start_idx; i < end_idx; i += step)
{
int peak = peak_tuple[i].to_int ();
if (!peak) continue;
@@ -333,6 +388,7 @@ struct TupleVariationHeader
TupleIndexMask = 0x0FFFu
};
+ TuppleIndex& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; }
DEFINE_SIZE_STATIC (2);
};
@@ -365,6 +421,468 @@ struct TupleVariationHeader
DEFINE_SIZE_MIN (4);
};
+/* not using hb_bytes_t: avoid potential build issues with some compilers */
+struct byte_data_t
+{
+ hb_bytes_t bytes;
+
+ byte_data_t () = default;
+ byte_data_t (const char *p_, unsigned len_) : bytes (hb_bytes_t (p_, len_)) {}
+
+ void fini () { bytes.fini (); }
+
+ bool operator == (const byte_data_t& o) const
+ { return bytes.arrayZ == o.bytes.arrayZ && bytes.length == o.bytes.length; }
+
+ explicit operator bool () const { return bytes.length; }
+
+ void copy (hb_serialize_context_t *c) const
+ { c->embed (bytes.arrayZ, bytes.length); }
+};
+
+enum packed_delta_flag_t
+{
+ DELTAS_ARE_ZERO = 0x80,
+ DELTAS_ARE_WORDS = 0x40,
+ DELTA_RUN_COUNT_MASK = 0x3F
+};
+
+struct tuple_delta_t
+{
+ public:
+ hb_hashmap_t<hb_tag_t, Triple> axis_tuples;
+
+ /* indices_length = point_count, indice[i] = 1 means point i is referenced */
+ hb_vector_t<bool> indices;
+
+ hb_vector_t<float> deltas_x;
+ /* empty for cvar tuples */
+ hb_vector_t<float> deltas_y;
+
+ /* compiled data: header and deltas
+ * compiled point data is saved in a hashmap within tuple_variations_t cause
+ * some point sets might be reused by different tuple variations */
+ hb_vector_t<char> compiled_tuple_header;
+ hb_vector_t<char> compiled_deltas;
+
+ tuple_delta_t () = default;
+ tuple_delta_t (const tuple_delta_t& o) = default;
+
+ tuple_delta_t (tuple_delta_t&& o) : tuple_delta_t ()
+ {
+ axis_tuples = std::move (o.axis_tuples);
+ indices = std::move (o.indices);
+ deltas_x = std::move (o.deltas_x);
+ deltas_y = std::move (o.deltas_y);
+ }
+
+ tuple_delta_t& operator = (tuple_delta_t&& o)
+ {
+ hb_swap (*this, o);
+ return *this;
+ }
+
+ void remove_axis (hb_tag_t axis_tag)
+ { axis_tuples.del (axis_tag); }
+
+ bool set_tent (hb_tag_t axis_tag, Triple tent)
+ { return axis_tuples.set (axis_tag, tent); }
+
+ tuple_delta_t& operator += (const tuple_delta_t& o)
+ {
+ unsigned num = indices.length;
+ for (unsigned i = 0; i < num; i++)
+ {
+ if (indices.arrayZ[i])
+ {
+ if (o.indices.arrayZ[i])
+ {
+ deltas_x[i] += o.deltas_x[i];
+ if (deltas_y && o.deltas_y)
+ deltas_y[i] += o.deltas_y[i];
+ }
+ }
+ else
+ {
+ if (!o.indices.arrayZ[i]) continue;
+ deltas_x[i] = o.deltas_x[i];
+ if (deltas_y && o.deltas_y)
+ deltas_y[i] = o.deltas_y[i];
+ }
+ }
+ return *this;
+ }
+
+ tuple_delta_t& operator *= (float scalar)
+ {
+ if (scalar == 1.0f)
+ return *this;
+
+ unsigned num = indices.length;
+ for (unsigned i = 0; i < num; i++)
+ {
+ if (!indices.arrayZ[i]) continue;
+
+ deltas_x[i] *= scalar;
+ if (deltas_y)
+ deltas_y[i] *= scalar;
+ }
+ return *this;
+ }
+
+ hb_vector_t<tuple_delta_t> change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit) const
+ {
+ hb_vector_t<tuple_delta_t> out;
+ Triple *tent;
+ if (!axis_tuples.has (axis_tag, &tent))
+ {
+ out.push (*this);
+ return out;
+ }
+
+ if ((tent->minimum < 0.f && tent->maximum > 0.f) ||
+ !(tent->minimum <= tent->middle && tent->middle <= tent->maximum))
+ return out;
+
+ if (tent->middle == 0.f)
+ {
+ out.push (*this);
+ return out;
+ }
+
+ result_t solutions = rebase_tent (*tent, axis_limit);
+ for (auto t : solutions)
+ {
+ tuple_delta_t new_var = *this;
+ if (t.second == Triple ())
+ new_var.remove_axis (axis_tag);
+ else
+ new_var.set_tent (axis_tag, t.second);
+
+ new_var *= t.first;
+ out.push (std::move (new_var));
+ }
+
+ return out;
+ }
+
+ /* deltas should be compiled already before we compile tuple
+ * variation header cause we need to fill in the size of the
+ * serialized data for this tuple variation */
+ //TODO(qxliu):add option to use sharedTuples in gvar
+ bool compile_tuple_var_header (const hb_map_t& axes_index_map,
+ unsigned points_data_length,
+ const hb_map_t& axes_old_index_tag_map)
+ {
+ if (!compiled_deltas) return false;
+
+ unsigned cur_axis_count = axes_index_map.get_population ();
+ /* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */
+ unsigned alloc_len = 3 * cur_axis_count * (F2DOT14::static_size) + 4;
+ if (unlikely (!compiled_tuple_header.resize (alloc_len))) return false;
+
+ unsigned flag = 0;
+ /* skip the first 4 header bytes: variationDataSize+tupleIndex */
+ F2DOT14* p = reinterpret_cast<F2DOT14 *> (compiled_tuple_header.begin () + 4);
+ F2DOT14* end = reinterpret_cast<F2DOT14 *> (compiled_tuple_header.end ());
+ hb_array_t<F2DOT14> coords (p, end - p);
+
+ /* encode peak coords */
+ unsigned peak_count = encode_peak_coords(coords, flag, axes_index_map, axes_old_index_tag_map);
+ if (!peak_count) return false;
+
+ /* encode interim coords, it's optional so returned num could be 0 */
+ unsigned interim_count = encode_interm_coords (coords.sub_array (peak_count), flag, axes_index_map, axes_old_index_tag_map);
+
+ //TODO(qxliu): add option to use shared_points in gvar
+ flag |= TupleVariationHeader::TuppleIndex::PrivatePointNumbers;
+
+ unsigned serialized_data_size = points_data_length + compiled_deltas.length;
+ TupleVariationHeader *o = reinterpret_cast<TupleVariationHeader *> (compiled_tuple_header.begin ());
+ o->varDataSize = serialized_data_size;
+ o->tupleIndex = flag;
+
+ unsigned total_header_len = 4 + (peak_count + interim_count) * (F2DOT14::static_size);
+ return compiled_tuple_header.resize (total_header_len);
+ }
+
+ unsigned encode_peak_coords (hb_array_t<F2DOT14> peak_coords,
+ unsigned& flag,
+ const hb_map_t& axes_index_map,
+ const hb_map_t& axes_old_index_tag_map) const
+ {
+ unsigned orig_axis_count = axes_old_index_tag_map.get_population ();
+ auto it = peak_coords.iter ();
+ unsigned count = 0;
+ for (unsigned i = 0; i < orig_axis_count; i++)
+ {
+ if (!axes_index_map.has (i)) /* axis pinned */
+ continue;
+ hb_tag_t axis_tag = axes_old_index_tag_map.get (i);
+ Triple *coords;
+ if (!axis_tuples.has (axis_tag, &coords))
+ (*it).set_int (0);
+ else
+ (*it).set_float (coords->middle);
+ it++;
+ count++;
+ }
+ flag |= TupleVariationHeader::TuppleIndex::EmbeddedPeakTuple;
+ return count;
+ }
+
+ /* if no need to encode intermediate coords, then just return p */
+ unsigned encode_interm_coords (hb_array_t<F2DOT14> coords,
+ unsigned& flag,
+ const hb_map_t& axes_index_map,
+ const hb_map_t& axes_old_index_tag_map) const
+ {
+ unsigned orig_axis_count = axes_old_index_tag_map.get_population ();
+ unsigned cur_axis_count = axes_index_map.get_population ();
+
+ auto start_coords_iter = coords.sub_array (0, cur_axis_count).iter ();
+ auto end_coords_iter = coords.sub_array (cur_axis_count).iter ();
+ bool encode_needed = false;
+ unsigned count = 0;
+ for (unsigned i = 0; i < orig_axis_count; i++)
+ {
+ if (!axes_index_map.has (i)) /* axis pinned */
+ continue;
+ hb_tag_t axis_tag = axes_old_index_tag_map.get (i);
+ Triple *coords;
+ float min_val = 0.f, val = 0.f, max_val = 0.f;
+ if (axis_tuples.has (axis_tag, &coords))
+ {
+ min_val = coords->minimum;
+ val = coords->middle;
+ max_val = coords->maximum;
+ }
+
+ (*start_coords_iter).set_float (min_val);
+ (*end_coords_iter).set_float (max_val);
+
+ start_coords_iter++;
+ end_coords_iter++;
+ count += 2;
+ if (min_val != hb_min (val, 0.f) || max_val != hb_max (val, 0.f))
+ encode_needed = true;
+ }
+
+ if (encode_needed)
+ {
+ flag |= TupleVariationHeader::TuppleIndex::IntermediateRegion;
+ return count;
+ }
+ return 0;
+ }
+
+ bool compile_deltas ()
+ {
+ hb_vector_t<int> rounded_deltas;
+ if (unlikely (!rounded_deltas.alloc (indices.length)))
+ return false;
+
+ for (unsigned i = 0; i < indices.length; i++)
+ {
+ if (!indices[i]) continue;
+ int rounded_delta = (int) roundf (deltas_x[i]);
+ rounded_deltas.push (rounded_delta);
+ }
+
+ if (!rounded_deltas) return false;
+ /* allocate enough memories 3 * num_deltas */
+ unsigned alloc_len = 3 * rounded_deltas.length;
+ if (deltas_y)
+ alloc_len *= 2;
+
+ if (unlikely (!compiled_deltas.resize (alloc_len))) return false;
+
+ unsigned i = 0;
+ unsigned encoded_len = encode_delta_run (i, compiled_deltas.as_array (), rounded_deltas);
+
+ if (deltas_y)
+ {
+ /* reuse the rounded_deltas vector, check that deltas_y have the same num of deltas as deltas_x */
+ unsigned j = 0;
+ for (unsigned idx = 0; idx < indices.length; idx++)
+ {
+ if (!indices[idx]) continue;
+ int rounded_delta = (int) roundf (deltas_y[idx]);
+
+ if (j >= rounded_deltas.length) return false;
+
+ rounded_deltas[j++] = rounded_delta;
+ }
+
+ if (j != rounded_deltas.length) return false;
+ encoded_len += encode_delta_run (i, compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas);
+ }
+ return compiled_deltas.resize (encoded_len);
+ }
+
+ unsigned encode_delta_run (unsigned& i,
+ hb_array_t<char> encoded_bytes,
+ const hb_vector_t<int>& deltas) const
+ {
+ unsigned num_deltas = deltas.length;
+ unsigned encoded_len = 0;
+ while (i < num_deltas)
+ {
+ int val = deltas[i];
+ if (val == 0)
+ encoded_len += encode_delta_run_as_zeroes (i, encoded_bytes.sub_array (encoded_len), deltas);
+ else if (val >= -128 && val <= 127)
+ encoded_len += encode_delta_run_as_bytes (i, encoded_bytes.sub_array (encoded_len), deltas);
+ else
+ encoded_len += encode_delta_run_as_words (i, encoded_bytes.sub_array (encoded_len), deltas);
+ }
+ return encoded_len;
+ }
+
+ unsigned encode_delta_run_as_zeroes (unsigned& i,
+ hb_array_t<char> encoded_bytes,
+ const hb_vector_t<int>& deltas) const
+ {
+ unsigned num_deltas = deltas.length;
+ unsigned run_length = 0;
+ auto it = encoded_bytes.iter ();
+ unsigned encoded_len = 0;
+ while (i < num_deltas && deltas[i] == 0)
+ {
+ i++;
+ run_length++;
+ }
+
+ while (run_length >= 64)
+ {
+ *it++ = (DELTAS_ARE_ZERO | 63);
+ run_length -= 64;
+ encoded_len++;
+ }
+
+ if (run_length)
+ {
+ *it++ = (DELTAS_ARE_ZERO | (run_length - 1));
+ encoded_len++;
+ }
+ return encoded_len;
+ }
+
+ unsigned encode_delta_run_as_bytes (unsigned &i,
+ hb_array_t<char> encoded_bytes,
+ const hb_vector_t<int>& deltas) const
+ {
+ unsigned start = i;
+ unsigned num_deltas = deltas.length;
+ while (i < num_deltas)
+ {
+ int val = deltas[i];
+ if (val > 127 || val < -128)
+ break;
+
+ /* from fonttools: if there're 2 or more zeros in a sequence,
+ * it is better to start a new run to save bytes. */
+ if (val == 0 && i + 1 < num_deltas && deltas[i+1] == 0)
+ break;
+
+ i++;
+ }
+ unsigned run_length = i - start;
+
+ unsigned encoded_len = 0;
+ auto it = encoded_bytes.iter ();
+
+ while (run_length >= 64)
+ {
+ *it++ = 63;
+ encoded_len++;
+
+ for (unsigned j = 0; j < 64; j++)
+ {
+ *it++ = static_cast<char> (deltas[start + j]);
+ encoded_len++;
+ }
+
+ start += 64;
+ run_length -= 64;
+ }
+
+ if (run_length)
+ {
+ *it++ = run_length - 1;
+ encoded_len++;
+
+ while (start < i)
+ {
+ *it++ = static_cast<char> (deltas[start++]);
+ encoded_len++;
+ }
+ }
+
+ return encoded_len;
+ }
+
+ unsigned encode_delta_run_as_words (unsigned &i,
+ hb_array_t<char> encoded_bytes,
+ const hb_vector_t<int>& deltas) const
+ {
+ unsigned start = i;
+ unsigned num_deltas = deltas.length;
+ while (i < num_deltas)
+ {
+ int val = deltas[i];
+
+ /* start a new run for a single zero value*/
+ if (val == 0) break;
+
+ /* from fonttools: continue word-encoded run if there's only one
+ * single value in the range [-128, 127] because it is more compact.
+ * Only start a new run when there're 2 continuous such values. */
+ if (val >= -128 && val <= 127 &&
+ i + 1 < num_deltas &&
+ deltas[i+1] >= -128 && deltas[i+1] <= 127)
+ break;
+
+ i++;
+ }
+
+ unsigned run_length = i - start;
+ auto it = encoded_bytes.iter ();
+ unsigned encoded_len = 0;
+ while (run_length >= 64)
+ {
+ *it++ = (DELTAS_ARE_WORDS | 63);
+ encoded_len++;
+
+ for (unsigned j = 0; j < 64; j++)
+ {
+ int16_t delta_val = deltas[start + j];
+ *it++ = static_cast<char> (delta_val >> 8);
+ *it++ = static_cast<char> (delta_val & 0xFF);
+
+ encoded_len += 2;
+ }
+
+ start += 64;
+ run_length -= 64;
+ }
+
+ if (run_length)
+ {
+ *it++ = (DELTAS_ARE_WORDS | (run_length - 1));
+ while (start < i)
+ {
+ int16_t delta_val = deltas[start++];
+ *it++ = static_cast<char> (delta_val >> 8);
+ *it++ = static_cast<char> (delta_val & 0xFF);
+
+ encoded_len += 2;
+ }
+ }
+ return encoded_len;
+ }
+};
+
struct TupleVariationData
{
bool sanitize (hb_sanitize_context_t *c) const
@@ -378,7 +896,7 @@ struct TupleVariationData
unsigned get_size (unsigned axis_count) const
{
unsigned total_size = min_size;
- unsigned count = tupleVarCount;
+ unsigned count = tupleVarCount.get_count ();
const TupleVariationHeader *tuple_var_header = &(get_tuple_var_header());
for (unsigned i = 0; i < count; i++)
{
@@ -392,8 +910,354 @@ struct TupleVariationData
const TupleVariationHeader &get_tuple_var_header (void) const
{ return StructAfter<TupleVariationHeader> (data); }
+ struct tuple_iterator_t;
+ struct tuple_variations_t
+ {
+ hb_vector_t<tuple_delta_t> tuple_vars;
+
+ private:
+ /* referenced point set->compiled point data map */
+ hb_hashmap_t<const hb_vector_t<bool>*, byte_data_t> point_data_map;
+ /* referenced point set-> count map, used in finding shared points */
+ hb_hashmap_t<const hb_vector_t<bool>*, unsigned> point_set_count_map;
+
+ public:
+ ~tuple_variations_t () { fini (); }
+ void fini ()
+ {
+ for (auto _ : point_data_map.values ())
+ _.fini ();
+
+ point_set_count_map.fini ();
+ tuple_vars.fini ();
+ }
+
+ unsigned get_var_count () const
+ { return tuple_vars.length; }
+
+ bool create_from_tuple_var_data (tuple_iterator_t iterator,
+ unsigned tuple_var_count,
+ unsigned point_count,
+ bool is_gvar,
+ const hb_map_t *axes_old_index_tag_map,
+ const hb_vector_t<unsigned> &shared_indices,
+ const hb_array_t<const F2DOT14> shared_tuples)
+ {
+ do
+ {
+ const HBUINT8 *p = iterator.get_serialized_data ();
+ unsigned int length = iterator.current_tuple->get_data_size ();
+ if (unlikely (!iterator.var_data_bytes.check_range (p, length)))
+ { fini (); return false; }
+
+ hb_hashmap_t<hb_tag_t, Triple> axis_tuples;
+ if (!iterator.current_tuple->unpack_axis_tuples (iterator.get_axis_count (), shared_tuples, axes_old_index_tag_map, axis_tuples)
+ || axis_tuples.is_empty ())
+ { fini (); return false; }
+
+ hb_vector_t<unsigned> private_indices;
+ bool has_private_points = iterator.current_tuple->has_private_points ();
+ const HBUINT8 *end = p + length;
+ if (has_private_points &&
+ !TupleVariationData::unpack_points (p, private_indices, end))
+ { fini (); return false; }
+
+ const hb_vector_t<unsigned> &indices = has_private_points ? private_indices : shared_indices;
+ bool apply_to_all = (indices.length == 0);
+ unsigned num_deltas = apply_to_all ? point_count : indices.length;
+
+ hb_vector_t<int> deltas_x;
+
+ if (unlikely (!deltas_x.resize (num_deltas, false) ||
+ !TupleVariationData::unpack_deltas (p, deltas_x, end)))
+ { fini (); return false; }
+
+ hb_vector_t<int> deltas_y;
+ if (is_gvar)
+ {
+ if (unlikely (!deltas_y.resize (num_deltas, false) ||
+ !TupleVariationData::unpack_deltas (p, deltas_y, end)))
+ { fini (); return false; }
+ }
+
+ tuple_delta_t var;
+ var.axis_tuples = std::move (axis_tuples);
+ if (unlikely (!var.indices.resize (point_count) ||
+ !var.deltas_x.resize (point_count, false)))
+ { fini (); return false; }
+
+ if (is_gvar && unlikely (!var.deltas_y.resize (point_count, false)))
+ { fini (); return false; }
+
+ for (unsigned i = 0; i < num_deltas; i++)
+ {
+ unsigned idx = apply_to_all ? i : indices[i];
+ if (idx >= point_count) continue;
+ var.indices[idx] = true;
+ var.deltas_x[idx] = static_cast<float> (deltas_x[i]);
+ if (is_gvar)
+ var.deltas_y[idx] = static_cast<float> (deltas_y[i]);
+ }
+ tuple_vars.push (std::move (var));
+ } while (iterator.move_to_next ());
+ return true;
+ }
+
+ void change_tuple_variations_axis_limits (const hb_hashmap_t<hb_tag_t, Triple> *normalized_axes_location)
+ {
+ for (auto _ : *normalized_axes_location)
+ {
+ hb_tag_t axis_tag = _.first;
+ Triple axis_limit = _.second;
+ hb_vector_t<tuple_delta_t> new_vars;
+ for (const tuple_delta_t& var : tuple_vars)
+ {
+ hb_vector_t<tuple_delta_t> out = var.change_tuple_var_axis_limit (axis_tag, axis_limit);
+ if (!out) continue;
+ unsigned new_len = new_vars.length + out.length;
+
+ if (unlikely (!new_vars.alloc (new_len, false)))
+ { fini (); return;}
+
+ for (unsigned i = 0; i < out.length; i++)
+ new_vars.push (std::move (out[i]));
+ }
+ tuple_vars.fini ();
+ tuple_vars = std::move (new_vars);
+ }
+ }
+
+ /* merge tuple variations with overlapping tents */
+ void merge_tuple_variations ()
+ {
+ hb_vector_t<tuple_delta_t> new_vars;
+ hb_hashmap_t<hb_hashmap_t<hb_tag_t, Triple>, unsigned> m;
+ unsigned i = 0;
+ for (const tuple_delta_t& var : tuple_vars)
+ {
+ /* if all axes are pinned, drop the tuple variation */
+ if (var.axis_tuples.is_empty ()) continue;
+
+ unsigned *idx;
+ if (m.has (var.axis_tuples, &idx))
+ {
+ new_vars[*idx] += var;
+ }
+ else
+ {
+ new_vars.push (var);
+ m.set (var.axis_tuples, i);
+ i++;
+ }
+ }
+ tuple_vars.fini ();
+ tuple_vars = std::move (new_vars);
+ }
+
+ byte_data_t compile_point_set (const hb_vector_t<bool> &point_indices)
+ {
+ unsigned num_points = 0;
+ for (bool i : point_indices)
+ if (i) num_points++;
+
+ unsigned indices_length = point_indices.length;
+ /* If the points set consists of all points in the glyph, it's encoded with a
+ * single zero byte */
+ if (num_points == indices_length)
+ {
+ char *p = (char *) hb_calloc (1, sizeof (char));
+ if (unlikely (!p)) return byte_data_t ();
+
+ return byte_data_t (p, 1);
+ }
+
+ /* allocate enough memories: 2 bytes for count + 3 bytes for each point */
+ unsigned num_bytes = 2 + 3 *num_points;
+ char *p = (char *) hb_calloc (num_bytes, sizeof (char));
+ if (unlikely (!p)) return byte_data_t ();
+
+ unsigned pos = 0;
+ /* binary data starts with the total number of reference points */
+ if (num_points < 0x80)
+ p[pos++] = num_points;
+ else
+ {
+ p[pos++] = ((num_points >> 8) | 0x80);
+ p[pos++] = num_points & 0xFF;
+ }
+
+ const unsigned max_run_length = 0x7F;
+ unsigned i = 0;
+ unsigned last_value = 0;
+ unsigned num_encoded = 0;
+ while (i < indices_length && num_encoded < num_points)
+ {
+ unsigned run_length = 0;
+ unsigned header_pos = pos;
+ p[pos++] = 0;
+
+ bool use_byte_encoding = false;
+ bool new_run = true;
+ while (i < indices_length && num_encoded < num_points &&
+ run_length <= max_run_length)
+ {
+ // find out next referenced point index
+ while (i < indices_length && !point_indices[i])
+ i++;
+
+ if (i >= indices_length) break;
+
+ unsigned cur_value = i;
+ unsigned delta = cur_value - last_value;
+
+ if (new_run)
+ {
+ use_byte_encoding = (delta <= 0xFF);
+ new_run = false;
+ }
+
+ if (use_byte_encoding && delta > 0xFF)
+ break;
+
+ if (use_byte_encoding)
+ p[pos++] = delta;
+ else
+ {
+ p[pos++] = delta >> 8;
+ p[pos++] = delta & 0xFF;
+ }
+ i++;
+ last_value = cur_value;
+ run_length++;
+ num_encoded++;
+ }
+
+ if (use_byte_encoding)
+ p[header_pos] = run_length - 1;
+ else
+ p[header_pos] = (run_length - 1) | 0x80;
+ }
+ return byte_data_t (p, pos);
+ }
+
+ /* compile all point set and store byte data in a point_set->byte_data_t hashmap,
+ * also update point_set->count map, which will be used in finding shared
+ * point set*/
+ bool compile_all_point_sets ()
+ {
+ for (const auto& tuple: tuple_vars)
+ {
+ const hb_vector_t<bool>* points_set = &(tuple.indices);
+ if (point_data_map.has (points_set))
+ {
+ unsigned *count;
+ if (unlikely (!point_set_count_map.has (points_set, &count) ||
+ !point_set_count_map.set (points_set, (*count) + 1)))
+ return false;
+ continue;
+ }
+
+ byte_data_t compiled_data = compile_point_set (*points_set);
+ if (unlikely (compiled_data == byte_data_t ()))
+ return false;
+
+ if (!point_data_map.set (points_set, compiled_data) ||
+ !point_set_count_map.set (points_set, 1))
+ return false;
+ }
+ return true;
+ }
+
+ /* find shared points set which saves most bytes */
+ byte_data_t find_shared_points ()
+ {
+ unsigned max_saved_bytes = 0;
+ byte_data_t res{};
+
+ for (const auto& _ : point_data_map.iter ())
+ {
+ const hb_vector_t<bool>* points_set = _.first;
+ unsigned data_length = _.second.bytes.length;
+ unsigned *count;
+ if (unlikely (!point_set_count_map.has (points_set, &count) ||
+ *count <= 1))
+ return byte_data_t ();
+
+ unsigned saved_bytes = data_length * ((*count) -1);
+ if (saved_bytes > max_saved_bytes)
+ {
+ max_saved_bytes = saved_bytes;
+ res = _.second;
+ }
+ }
+ return res;
+ }
+
+ void instantiate (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location)
+ {
+ change_tuple_variations_axis_limits (&normalized_axes_location);
+ merge_tuple_variations ();
+ }
+
+ bool compile_bytes (const hb_map_t& axes_index_map,
+ const hb_map_t& axes_old_index_tag_map)
+ {
+ // compile points set and store data in hashmap
+ if (!compile_all_point_sets ())
+ return false;
+ // compile delta and tuple var header for each tuple variation
+ for (auto& tuple: tuple_vars)
+ {
+ const hb_vector_t<bool>* points_set = &(tuple.indices);
+ byte_data_t *points_data;
+ if (unlikely (!point_data_map.has (points_set, &points_data)))
+ return false;
+
+ if (!tuple.compile_deltas ())
+ return false;
+
+ if (!tuple.compile_tuple_var_header (axes_index_map, points_data->bytes.length, axes_old_index_tag_map))
+ return false;
+ }
+ return true;
+ }
+
+ bool serialize_var_headers (hb_serialize_context_t *c, unsigned& total_header_len) const
+ {
+ TRACE_SERIALIZE (this);
+ for (const auto& tuple: tuple_vars)
+ {
+ byte_data_t compiled_bytes {tuple.compiled_tuple_header.arrayZ, tuple.compiled_tuple_header.length};
+ compiled_bytes.copy (c);
+ if (c->in_error ()) return_trace (false);
+ total_header_len += tuple.compiled_tuple_header.length;
+ }
+ return_trace (true);
+ }
+
+ bool serialize_var_data (hb_serialize_context_t *c) const
+ {
+ TRACE_SERIALIZE (this);
+ for (const auto& tuple: tuple_vars)
+ {
+ const hb_vector_t<bool>* points_set = &(tuple.indices);
+ byte_data_t *point_data;
+ if (!point_data_map.has (points_set, &point_data))
+ return_trace (false);
+
+ point_data->copy (c);
+ byte_data_t compiled_bytes {tuple.compiled_deltas.arrayZ, tuple.compiled_deltas.length};
+ compiled_bytes.copy (c);
+ if (c->in_error ()) return_trace (false);
+ }
+ return_trace (true);
+ }
+ };
+
struct tuple_iterator_t
{
+ unsigned get_axis_count () const { return axis_count; }
+
void init (hb_bytes_t var_data_bytes_, unsigned int axis_count_, const void *table_base_)
{
var_data_bytes = var_data_bytes_;
@@ -517,13 +1381,6 @@ struct TupleVariationData
hb_vector_t<int> &deltas /* IN/OUT */,
const HBUINT8 *end)
{
- enum packed_delta_flag_t
- {
- DELTAS_ARE_ZERO = 0x80,
- DELTAS_ARE_WORDS = 0x40,
- DELTA_RUN_COUNT_MASK = 0x3F
- };
-
unsigned i = 0;
unsigned count = deltas.length;
while (i < count)
@@ -561,11 +1418,50 @@ struct TupleVariationData
bool has_data () const { return tupleVarCount; }
+ bool decompile_tuple_variations (unsigned point_count,
+ bool is_gvar,
+ tuple_iterator_t iterator,
+ const hb_map_t *axes_old_index_tag_map,
+ const hb_vector_t<unsigned> &shared_indices,
+ const hb_array_t<const F2DOT14> shared_tuples,
+ tuple_variations_t& tuple_variations /* OUT */) const
+ {
+ return tuple_variations.create_from_tuple_var_data (iterator, tupleVarCount,
+ point_count, is_gvar,
+ axes_old_index_tag_map,
+ shared_indices,
+ shared_tuples);
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ bool is_gvar,
+ tuple_variations_t& tuple_variations) const
+ {
+ TRACE_SERIALIZE (this);
+ auto *out = c->start_embed (this);
+ if (unlikely (!c->extend_min (out))) return_trace (false);
+
+ if (!c->check_assign (out->tupleVarCount, tuple_variations.get_var_count (),
+ HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
+
+ unsigned total_header_len = 0;
+
+ if (!tuple_variations.serialize_var_headers (c, total_header_len))
+ return_trace (false);
+
+ unsigned data_offset = min_size + total_header_len;
+ if (!is_gvar) data_offset += 4;
+ if (!c->check_assign (out->data, data_offset, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
+
+ return tuple_variations.serialize_var_data (c);
+ }
+
protected:
struct TupleVarCount : HBUINT16
{
bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers); }
unsigned int get_count () const { return (*this) & CountMask; }
+ TupleVarCount& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; }
protected:
enum Flags
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-cvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-cvar-table.hh
index 7fd0f1d79d..de54339301 100644
--- a/thirdparty/harfbuzz/src/hb-ot-var-cvar-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-var-cvar-table.hh
@@ -27,6 +27,7 @@
#define HB_OT_VAR_CVAR_TABLE_HH
#include "hb-ot-var-common.hh"
+#include "hb-ot-var-fvar-table.hh"
namespace OT {
@@ -51,6 +52,27 @@ struct cvar
const TupleVariationData* get_tuple_var_data (void) const
{ return &tupleVariationData; }
+ bool decompile_tuple_variations (unsigned axis_count,
+ unsigned point_count,
+ bool is_gvar,
+ const hb_map_t *axes_old_index_tag_map,
+ TupleVariationData::tuple_variations_t& tuple_variations /* OUT */) const
+ {
+ hb_vector_t<unsigned> shared_indices;
+ TupleVariationData::tuple_iterator_t iterator;
+ unsigned var_data_length = tupleVariationData.get_size (axis_count);
+ hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast<const char*> (get_tuple_var_data ()), var_data_length);
+ if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, this,
+ shared_indices, &iterator))
+ return false;
+
+ return tupleVariationData.decompile_tuple_variations (point_count, is_gvar, iterator,
+ axes_old_index_tag_map,
+ shared_indices,
+ hb_array<const F2DOT14> (),
+ tuple_variations);
+ }
+
static bool calculate_cvt_deltas (unsigned axis_count,
hb_array_t<int> coords,
unsigned num_cvt_item,
@@ -104,6 +126,53 @@ struct cvar
return true;
}
+
+ bool serialize (hb_serialize_context_t *c,
+ TupleVariationData::tuple_variations_t& tuple_variations) const
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->embed (version))) return_trace (false);
+
+ return_trace (tupleVariationData.serialize (c, false, tuple_variations));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ if (c->plan->all_axes_pinned)
+ return_trace (false);
+
+ /* subset() for cvar is called by partial instancing only, we always pass
+ * through cvar table in other cases */
+ if (!c->plan->normalized_coords)
+ {
+ unsigned axis_count = c->plan->source->table.fvar->get_axis_count ();
+ unsigned total_size = min_size + tupleVariationData.get_size (axis_count);
+ char *out = c->serializer->allocate_size<char> (total_size);
+ if (unlikely (!out)) return_trace (false);
+
+ hb_memcpy (out, this, total_size);
+ return_trace (true);
+ }
+
+ OT::TupleVariationData::tuple_variations_t tuple_variations;
+ unsigned axis_count = c->plan->axes_old_index_tag_map.get_population ();
+
+ const hb_tag_t cvt = HB_TAG('c','v','t',' ');
+ hb_blob_t *cvt_blob = hb_face_reference_table (c->plan->source, cvt);
+ unsigned point_count = hb_blob_get_length (cvt_blob) / FWORD::static_size;
+
+ if (!decompile_tuple_variations (axis_count, point_count, false,
+ &(c->plan->axes_old_index_tag_map),
+ tuple_variations))
+ return_trace (false);
+
+ tuple_variations.instantiate (c->plan->axes_location);
+ if (!tuple_variations.compile_bytes (c->plan->axes_index_map, c->plan->axes_old_index_tag_map))
+ return_trace (false);
+
+ return_trace (serialize (c->serializer, tuple_variations));
+ }
static bool add_cvt_and_apply_deltas (hb_subset_plan_t *plan,
const TupleVariationData *tuple_var_data,
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh
index a384dfa531..d8e789cb44 100644
--- a/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh
@@ -39,6 +39,24 @@
namespace OT {
+static bool axis_coord_pinned_or_within_axis_range (const hb_array_t<const F16DOT16> coords,
+ unsigned axis_index,
+ Triple axis_limit)
+{
+ float axis_coord = coords[axis_index].to_float ();
+ if (axis_limit.is_point ())
+ {
+ if (axis_limit.minimum != axis_coord)
+ return false;
+ }
+ else
+ {
+ if (axis_coord < axis_limit.minimum ||
+ axis_coord > axis_limit.maximum)
+ return false;
+ }
+ return true;
+}
struct InstanceRecord
{
@@ -47,6 +65,27 @@ struct InstanceRecord
hb_array_t<const F16DOT16> get_coordinates (unsigned int axis_count) const
{ return coordinatesZ.as_array (axis_count); }
+ bool keep_instance (unsigned axis_count,
+ const hb_map_t *axes_index_tag_map,
+ const hb_hashmap_t<hb_tag_t, Triple> *axes_location) const
+ {
+ if (axes_location->is_empty ()) return true;
+ const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count);
+ for (unsigned i = 0 ; i < axis_count; i++)
+ {
+ uint32_t *axis_tag;
+ if (!axes_index_tag_map->has (i, &axis_tag))
+ return false;
+ if (!axes_location->has (*axis_tag))
+ continue;
+
+ Triple axis_limit = axes_location->get (*axis_tag);
+ if (!axis_coord_pinned_or_within_axis_range (coords, i, axis_limit))
+ return false;
+ }
+ return true;
+ }
+
bool subset (hb_subset_context_t *c,
unsigned axis_count,
bool has_postscript_nameid) const
@@ -56,19 +95,22 @@ struct InstanceRecord
if (unlikely (!c->serializer->embed (flags))) return_trace (false);
const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count);
- const hb_hashmap_t<hb_tag_t, float> *axes_location = &c->plan->user_axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple> *axes_location = &c->plan->user_axes_location;
for (unsigned i = 0 ; i < axis_count; i++)
{
uint32_t *axis_tag;
// only keep instances whose coordinates == pinned axis location
if (!c->plan->axes_old_index_tag_map.has (i, &axis_tag)) continue;
-
- if (axes_location->has (*axis_tag) &&
- fabsf (axes_location->get (*axis_tag) - coords[i].to_float ()) > 0.001f)
- return_trace (false);
-
- if (!c->plan->axes_index_map.has (i))
- continue;
+ if (axes_location->has (*axis_tag))
+ {
+ Triple axis_limit = axes_location->get (*axis_tag);
+ if (!axis_coord_pinned_or_within_axis_range (coords, i, axis_limit))
+ return_trace (false);
+
+ //skip pinned axis
+ if (axis_limit.is_point ())
+ continue;
+ }
if (!c->serializer->embed (coords[i]))
return_trace (false);
@@ -216,7 +258,8 @@ struct fvar
axisSize == 20 && /* Assumed in our code. */
instanceSize >= axisCount * 4 + 4 &&
get_axes ().sanitize (c) &&
- c->check_range (get_instance (0), instanceCount, instanceSize));
+ c->check_range (&StructAfter<InstanceRecord> (get_axes ()),
+ instanceCount, instanceSize));
}
unsigned int get_axis_count () const { return axisCount; }
@@ -314,21 +357,19 @@ struct fvar
return axisCount;
}
- void collect_name_ids (hb_hashmap_t<hb_tag_t, float> *user_axes_location,
+ void collect_name_ids (hb_hashmap_t<hb_tag_t, Triple> *user_axes_location,
+ hb_map_t *axes_old_index_tag_map,
hb_set_t *nameids /* IN/OUT */) const
{
if (!has_data ()) return;
- hb_map_t pinned_axes;
auto axis_records = get_axes ();
for (unsigned i = 0 ; i < (unsigned)axisCount; i++)
{
hb_tag_t axis_tag = axis_records[i].get_axis_tag ();
- if (user_axes_location->has (axis_tag))
- {
- pinned_axes.set (i, axis_tag);
+ if (user_axes_location->has (axis_tag) &&
+ user_axes_location->get (axis_tag).is_point ())
continue;
- }
nameids->add (axis_records[i].get_name_id ());
}
@@ -337,16 +378,7 @@ struct fvar
{
const InstanceRecord *instance = get_instance (i);
- if (hb_any (+ hb_enumerate (instance->get_coordinates (axisCount))
- | hb_filter (pinned_axes, hb_first)
- | hb_map ([&] (const hb_pair_t<unsigned, const F16DOT16&>& _)
- {
- hb_tag_t axis_tag = pinned_axes.get (_.first);
- float location = user_axes_location->get (axis_tag);
- if (fabs ((double)location - (double)_.second.to_float ()) > 0.001) return true;
- return false;
- })
- ))
+ if (!instance->keep_instance (axisCount, axes_old_index_tag_map, user_axes_location))
continue;
nameids->add (instance->subfamilyNameID);
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh
index ece892e1dd..b5099ac074 100644
--- a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh
@@ -50,13 +50,14 @@ struct contour_point_t
y = x * matrix[1] + y * matrix[3];
x = x_;
}
+ HB_ALWAYS_INLINE
void translate (const contour_point_t &p) { x += p.x; y += p.y; }
- float x = 0.f;
- float y = 0.f;
- uint8_t flag = 0;
- bool is_end_point = false;
+ float x;
+ float y;
+ uint8_t flag;
+ bool is_end_point;
};
struct contour_point_vector_t : hb_vector_t<contour_point_t>
@@ -110,20 +111,20 @@ struct gvar
unsigned int num_glyphs = c->plan->num_output_glyphs ();
out->glyphCountX = hb_min (0xFFFFu, num_glyphs);
+ auto it = hb_iter (c->plan->new_to_old_gid_list);
+ if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
+ it++;
unsigned int subset_data_size = 0;
- for (hb_codepoint_t gid = (c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE) ? 0 : 1;
- gid < num_glyphs;
- gid++)
+ for (auto &_ : it)
{
- hb_codepoint_t old_gid;
- if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue;
+ hb_codepoint_t old_gid = _.second;
subset_data_size += get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length;
}
bool long_offset = subset_data_size & ~0xFFFFu;
out->flags = long_offset ? 1 : 0;
- HBUINT8 *subset_offsets = c->serializer->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1));
+ HBUINT8 *subset_offsets = c->serializer->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1), false);
if (!subset_offsets) return_trace (false);
/* shared tuples */
@@ -138,36 +139,61 @@ struct gvar
hb_memcpy (tuples, this+sharedTuples, shared_tuple_size);
}
- char *subset_data = c->serializer->allocate_size<char> (subset_data_size);
+ char *subset_data = c->serializer->allocate_size<char> (subset_data_size, false);
if (!subset_data) return_trace (false);
out->dataZ = subset_data - (char *) out;
+
+ if (long_offset)
+ {
+ ((HBUINT32 *) subset_offsets)[0] = 0;
+ subset_offsets += 4;
+ }
+ else
+ {
+ ((HBUINT16 *) subset_offsets)[0] = 0;
+ subset_offsets += 2;
+ }
unsigned int glyph_offset = 0;
- for (hb_codepoint_t gid = (c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE) ? 0 : 1;
- gid < num_glyphs;
- gid++)
+
+ hb_codepoint_t last = 0;
+ it = hb_iter (c->plan->new_to_old_gid_list);
+ if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
+ it++;
+ for (auto &_ : it)
{
- hb_codepoint_t old_gid;
- hb_bytes_t var_data_bytes = c->plan->old_gid_for_new_gid (gid, &old_gid)
- ? get_glyph_var_data_bytes (c->source_blob,
+ hb_codepoint_t gid = _.first;
+ hb_codepoint_t old_gid = _.second;
+
+ if (long_offset)
+ for (; last < gid; last++)
+ ((HBUINT32 *) subset_offsets)[last] = glyph_offset;
+ else
+ for (; last < gid; last++)
+ ((HBUINT16 *) subset_offsets)[last] = glyph_offset / 2;
+
+ hb_bytes_t var_data_bytes = get_glyph_var_data_bytes (c->source_blob,
glyph_count,
- old_gid)
- : hb_bytes_t ();
+ old_gid);
+
+ hb_memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length);
+ subset_data += var_data_bytes.length;
+ glyph_offset += var_data_bytes.length;
if (long_offset)
((HBUINT32 *) subset_offsets)[gid] = glyph_offset;
else
((HBUINT16 *) subset_offsets)[gid] = glyph_offset / 2;
- if (var_data_bytes.length > 0)
- hb_memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length);
- subset_data += var_data_bytes.length;
- glyph_offset += var_data_bytes.length;
+ last++; // Skip over gid
}
+
if (long_offset)
- ((HBUINT32 *) subset_offsets)[num_glyphs] = glyph_offset;
+ for (; last < num_glyphs; last++)
+ ((HBUINT32 *) subset_offsets)[last] = glyph_offset;
else
- ((HBUINT16 *) subset_offsets)[num_glyphs] = glyph_offset / 2;
+ for (; last < num_glyphs; last++)
+ ((HBUINT16 *) subset_offsets)[last] = glyph_offset / 2;
return_trace (true);
}
@@ -216,21 +242,24 @@ struct gvar
for (unsigned i = 0; i < count; i++)
{
hb_array_t<const F2DOT14> tuple = shared_tuples.sub_array (axis_count * i, axis_count);
- int idx = -1;
+ int idx1 = -1, idx2 = -1;
for (unsigned j = 0; j < axis_count; j++)
{
const F2DOT14 &peak = tuple.arrayZ[j];
if (peak.to_int () != 0)
{
- if (idx != -1)
+ if (idx1 == -1)
+ idx1 = j;
+ else if (idx2 == -1)
+ idx2 = j;
+ else
{
- idx = -1;
+ idx1 = idx2 = -1;
break;
}
- idx = j;
}
}
- shared_tuple_active_idx.arrayZ[i] = idx;
+ shared_tuple_active_idx.arrayZ[i] = {idx1, idx2};
}
}
~accelerator_t () { table.destroy (); }
@@ -266,10 +295,9 @@ struct gvar
public:
bool apply_deltas_to_points (hb_codepoint_t glyph,
hb_array_t<int> coords,
- const hb_array_t<contour_point_t> points) const
+ const hb_array_t<contour_point_t> points,
+ bool phantom_only = false) const
{
- if (!coords) return true;
-
if (unlikely (glyph >= glyphCount)) return true;
hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), glyphCount, glyph);
@@ -297,6 +325,7 @@ struct gvar
hb_vector_t<unsigned int> private_indices;
hb_vector_t<int> x_deltas;
hb_vector_t<int> y_deltas;
+ unsigned count = points.length;
bool flush = false;
do
{
@@ -310,9 +339,10 @@ struct gvar
if (!deltas)
{
- if (unlikely (!deltas_vec.resize (points.length, false))) return false;
+ if (unlikely (!deltas_vec.resize (count, false))) return false;
deltas = deltas_vec.as_array ();
- hb_memset (deltas.arrayZ, 0, deltas.get_size ()); // Faster than vector resize
+ hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0,
+ (phantom_only ? 4 : count) * sizeof (deltas[0]));
}
const HBUINT8 *end = p + length;
@@ -332,7 +362,7 @@ struct gvar
if (!apply_to_all)
{
- if (!orig_points)
+ if (!orig_points && !phantom_only)
{
orig_points_vec.extend (points);
if (unlikely (orig_points_vec.in_error ())) return false;
@@ -341,13 +371,13 @@ struct gvar
if (flush)
{
- unsigned count = points.length;
- for (unsigned int i = 0; i < count; i++)
+ for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
points.arrayZ[i].translate (deltas.arrayZ[i]);
flush = false;
}
- hb_memset (deltas.arrayZ, 0, deltas.get_size ());
+ hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0,
+ (phantom_only ? 4 : count) * sizeof (deltas[0]));
}
if (HB_OPTIMIZE_SIZE_VAL)
@@ -362,6 +392,7 @@ struct gvar
pt_index = indices[i];
if (unlikely (pt_index >= deltas.length)) continue;
}
+ if (phantom_only && pt_index < count - 4) continue;
auto &delta = deltas.arrayZ[pt_index];
delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */
delta.x += x_deltas.arrayZ[i] * scalar;
@@ -374,7 +405,7 @@ struct gvar
if (scalar != 1.0f)
{
if (apply_to_all)
- for (unsigned int i = 0; i < num_deltas; i++)
+ for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
{
unsigned int pt_index = i;
auto &delta = deltas.arrayZ[pt_index];
@@ -386,6 +417,7 @@ struct gvar
{
unsigned int pt_index = indices[i];
if (unlikely (pt_index >= deltas.length)) continue;
+ if (phantom_only && pt_index < count - 4) continue;
auto &delta = deltas.arrayZ[pt_index];
delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */
delta.x += x_deltas.arrayZ[i] * scalar;
@@ -395,7 +427,7 @@ struct gvar
else
{
if (apply_to_all)
- for (unsigned int i = 0; i < num_deltas; i++)
+ for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
{
unsigned int pt_index = i;
auto &delta = deltas.arrayZ[pt_index];
@@ -407,6 +439,7 @@ struct gvar
{
unsigned int pt_index = indices[i];
if (unlikely (pt_index >= deltas.length)) continue;
+ if (phantom_only && pt_index < count - 4) continue;
auto &delta = deltas.arrayZ[pt_index];
delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */
delta.x += x_deltas.arrayZ[i];
@@ -416,11 +449,10 @@ struct gvar
}
/* infer deltas for unreferenced points */
- if (!apply_to_all)
+ if (!apply_to_all && !phantom_only)
{
if (!end_points)
{
- unsigned count = points.length;
for (unsigned i = 0; i < count; ++i)
if (points.arrayZ[i].is_end_point)
end_points.push (i);
@@ -482,8 +514,7 @@ struct gvar
if (flush)
{
- unsigned count = points.length;
- for (unsigned int i = 0; i < count; i++)
+ for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
points.arrayZ[i].translate (deltas.arrayZ[i]);
}
@@ -495,7 +526,7 @@ struct gvar
private:
hb_blob_ptr_t<gvar> table;
unsigned glyphCount;
- hb_vector_t<signed> shared_tuple_active_idx;
+ hb_vector_t<hb_pair_t<int, int>> shared_tuple_active_idx;
};
protected:
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh
index 461b2b9cdb..943d376bdf 100644
--- a/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh
@@ -56,52 +56,56 @@ struct index_map_subset_plan_t
if (&index_map == &Null (DeltaSetIndexMap)) return;
unsigned int last_val = (unsigned int)-1;
- hb_codepoint_t last_gid = (hb_codepoint_t)-1;
- hb_codepoint_t gid = (hb_codepoint_t) hb_min (index_map.get_map_count (), plan->num_output_glyphs ());
+ hb_codepoint_t last_gid = HB_CODEPOINT_INVALID;
+ hb_codepoint_t num_gid = (hb_codepoint_t) hb_min (index_map.get_map_count (), plan->num_output_glyphs ());
outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bit_count ();
max_inners.resize (inner_sets.length);
for (unsigned i = 0; i < inner_sets.length; i++) max_inners[i] = 0;
/* Search backwards for a map value different from the last map value */
- for (; gid > 0; gid--)
+ auto &new_to_old_gid_list = plan->new_to_old_gid_list;
+ unsigned count = new_to_old_gid_list.length;
+ for (unsigned j = count; j; j--)
{
- hb_codepoint_t old_gid;
- if (!plan->old_gid_for_new_gid (gid - 1, &old_gid))
- {
- if (last_gid == (hb_codepoint_t) -1)
- continue;
- else
- break;
- }
+ hb_codepoint_t gid = new_to_old_gid_list[j - 1].first;
+ if (gid >= num_gid) continue;
+
+ hb_codepoint_t old_gid = new_to_old_gid_list[j - 1].second;
unsigned int v = index_map.map (old_gid);
- if (last_gid == (hb_codepoint_t) -1)
+ if (last_gid == HB_CODEPOINT_INVALID)
{
+ if (gid + 1 != num_gid)
+ {
+ last_gid = gid + 1;
+ break;
+ }
last_val = v;
last_gid = gid;
continue;
}
- if (v != last_val) break;
+ if (v != last_val || gid + 1 != last_gid)
+ break;
last_gid = gid;
}
if (unlikely (last_gid == (hb_codepoint_t)-1)) return;
- map_count = last_gid;
- for (gid = 0; gid < map_count; gid++)
+ map_count = last_gid + 1;
+ for (auto _ : plan->new_to_old_gid_list)
{
- hb_codepoint_t old_gid;
- if (plan->old_gid_for_new_gid (gid, &old_gid))
- {
- unsigned int v = index_map.map (old_gid);
- unsigned int outer = v >> 16;
- unsigned int inner = v & 0xFFFF;
- outer_map.add (outer);
- if (inner > max_inners[outer]) max_inners[outer] = inner;
- if (outer >= inner_sets.length) return;
- inner_sets[outer]->add (inner);
- }
+ hb_codepoint_t gid = _.first;
+ if (gid >= map_count) break;
+
+ hb_codepoint_t old_gid = _.second;
+ unsigned int v = index_map.map (old_gid);
+ unsigned int outer = v >> 16;
+ unsigned int inner = v & 0xFFFF;
+ outer_map.add (outer);
+ if (inner > max_inners[outer]) max_inners[outer] = inner;
+ if (outer >= inner_sets.length) return;
+ inner_sets[outer]->add (inner);
}
}
@@ -197,7 +201,8 @@ struct hvarvvar_subset_plan_t
if (retain_adv_map)
{
- for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++)
+ unsigned num_glyphs = plan->num_output_glyphs ();
+ for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
{
if (inner_sets[0]->has (gid))
inner_maps[0].add (gid);
diff --git a/thirdparty/harfbuzz/src/hb-ot-vorg-table.hh b/thirdparty/harfbuzz/src/hb-ot-vorg-table.hh
index 811e13919e..671b6d2c29 100644
--- a/thirdparty/harfbuzz/src/hb-ot-vorg-table.hh
+++ b/thirdparty/harfbuzz/src/hb-ot-vorg-table.hh
@@ -90,7 +90,7 @@ struct VORG
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
- VORG *vorg_prime = c->serializer->start_embed<VORG> ();
+ auto *vorg_prime = c->serializer->start_embed<VORG> ();
if (unlikely (!c->serializer->check_success (vorg_prime))) return_trace (false);
auto it =
diff --git a/thirdparty/harfbuzz/src/hb-pool.hh b/thirdparty/harfbuzz/src/hb-pool.hh
index d6eb778f5d..fcf10666b0 100644
--- a/thirdparty/harfbuzz/src/hb-pool.hh
+++ b/thirdparty/harfbuzz/src/hb-pool.hh
@@ -58,7 +58,7 @@ struct hb_pool_t
if (unlikely (!next))
{
if (unlikely (!chunks.alloc (chunks.length + 1))) return nullptr;
- chunk_t *chunk = (chunk_t *) hb_calloc (1, sizeof (chunk_t));
+ chunk_t *chunk = (chunk_t *) hb_malloc (sizeof (chunk_t));
if (unlikely (!chunk)) return nullptr;
chunks.push (chunk);
next = chunk->thread ();
diff --git a/thirdparty/harfbuzz/src/hb-sanitize.hh b/thirdparty/harfbuzz/src/hb-sanitize.hh
index 5259891b7e..2d338c51c8 100644
--- a/thirdparty/harfbuzz/src/hb-sanitize.hh
+++ b/thirdparty/harfbuzz/src/hb-sanitize.hh
@@ -122,12 +122,14 @@ struct hb_sanitize_context_t :
{
hb_sanitize_context_t () :
start (nullptr), end (nullptr),
+ length (0),
max_ops (0), max_subtables (0),
recursion_depth (0),
writable (false), edit_count (0),
blob (nullptr),
num_glyphs (65536),
- num_glyphs_set (false) {}
+ num_glyphs_set (false),
+ lazy_some_gpos (false) {}
const char *get_name () { return "SANITIZE"; }
template <typename T, typename F>
@@ -155,6 +157,19 @@ struct hb_sanitize_context_t :
dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
+ hb_sanitize_context_t (hb_blob_t *b) : hb_sanitize_context_t ()
+ {
+ init (b);
+
+ if (blob)
+ start_processing ();
+ }
+
+ ~hb_sanitize_context_t ()
+ {
+ if (blob)
+ end_processing ();
+ }
void init (hb_blob_t *b)
{
@@ -180,11 +195,15 @@ struct hb_sanitize_context_t :
const char *obj_start = (const char *) obj;
if (unlikely (obj_start < this->start || this->end <= obj_start))
+ {
this->start = this->end = nullptr;
+ this->length = 0;
+ }
else
{
this->start = obj_start;
this->end = obj_start + hb_min (size_t (this->end - obj_start), obj->get_size ());
+ this->length = this->end - this->start;
}
}
@@ -192,6 +211,7 @@ struct hb_sanitize_context_t :
{
this->start = this->blob->data;
this->end = this->start + this->blob->length;
+ this->length = this->end - this->start;
assert (this->start <= this->end); /* Must not overflow. */
}
@@ -224,6 +244,7 @@ struct hb_sanitize_context_t :
hb_blob_destroy (this->blob);
this->blob = nullptr;
this->start = this->end = nullptr;
+ this->length = 0;
}
unsigned get_edit_count () { return edit_count; }
@@ -240,15 +261,16 @@ struct hb_sanitize_context_t :
return (this->max_ops -= (int) count) > 0;
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool check_range (const void *base,
unsigned int len) const
{
const char *p = (const char *) base;
- bool ok = !len ||
- (this->start <= p &&
- p <= this->end &&
- (unsigned int) (this->end - p) >= len &&
- (this->max_ops -= len) > 0);
+ bool ok = (uintptr_t) (p - this->start) <= this->length &&
+ (unsigned int) (this->end - p) >= len &&
+ ((this->max_ops -= len) > 0);
DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
"check_range [%p..%p]"
@@ -259,6 +281,43 @@ struct hb_sanitize_context_t :
return likely (ok);
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
+ bool check_range_fast (const void *base,
+ unsigned int len) const
+ {
+ const char *p = (const char *) base;
+ bool ok = ((uintptr_t) (p - this->start) <= this->length &&
+ (unsigned int) (this->end - p) >= len);
+
+ DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+ "check_range_fast [%p..%p]"
+ " (%u bytes) in [%p..%p] -> %s",
+ p, p + len, len,
+ this->start, this->end,
+ ok ? "OK" : "OUT-OF-RANGE");
+
+ return likely (ok);
+ }
+
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
+ bool check_point (const void *base) const
+ {
+ const char *p = (const char *) base;
+ bool ok = (uintptr_t) (p - this->start) <= this->length;
+
+ DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+ "check_point [%p]"
+ " in [%p..%p] -> %s",
+ p,
+ this->start, this->end,
+ ok ? "OK" : "OUT-OF-RANGE");
+
+ return likely (ok);
+ }
template <typename T>
bool check_range (const T *base,
@@ -282,6 +341,20 @@ struct hb_sanitize_context_t :
}
template <typename T>
+ HB_ALWAYS_INLINE
+ bool check_array_sized (const T *base, unsigned int len, unsigned len_size) const
+ {
+ if (len_size >= 4)
+ {
+ if (unlikely (hb_unsigned_mul_overflows (len, hb_static_size (T), &len)))
+ return false;
+ }
+ else
+ len = len * hb_static_size (T);
+ return this->check_range (base, len);
+ }
+
+ template <typename T>
bool check_array (const T *base, unsigned int len) const
{
return this->check_range (base, len, hb_static_size (T));
@@ -292,7 +365,7 @@ struct hb_sanitize_context_t :
unsigned int a,
unsigned int b) const
{
- return this->check_range (base, a, b, hb_static_size (T));
+ return this->check_range (base, hb_static_size (T), a, b);
}
bool check_start_recursion (int max_depth)
@@ -309,7 +382,12 @@ struct hb_sanitize_context_t :
template <typename Type>
bool check_struct (const Type *obj) const
- { return likely (this->check_range (obj, obj->min_size)); }
+ {
+ if (sizeof (uintptr_t) == sizeof (uint32_t))
+ return likely (this->check_range_fast (obj, obj->min_size));
+ else
+ return likely (this->check_point ((const char *) obj + obj->min_size));
+ }
bool may_edit (const void *base, unsigned int len)
{
@@ -416,6 +494,7 @@ struct hb_sanitize_context_t :
}
const char *start, *end;
+ unsigned length;
mutable int max_ops, max_subtables;
private:
int recursion_depth;
@@ -424,6 +503,8 @@ struct hb_sanitize_context_t :
hb_blob_t *blob;
unsigned int num_glyphs;
bool num_glyphs_set;
+ public:
+ bool lazy_some_gpos;
};
struct hb_sanitize_with_object_t
diff --git a/thirdparty/harfbuzz/src/hb-serialize.hh b/thirdparty/harfbuzz/src/hb-serialize.hh
index 61ec0253a0..f852f07ba6 100644
--- a/thirdparty/harfbuzz/src/hb-serialize.hh
+++ b/thirdparty/harfbuzz/src/hb-serialize.hh
@@ -113,7 +113,7 @@ struct hb_serialize_context_t
{
// Virtual links aren't considered for equality since they don't affect the functionality
// of the object.
- return hb_bytes_t (head, tail - head).hash () ^
+ return hb_bytes_t (head, hb_min (128, tail - head)).hash () ^
real_links.as_bytes ().hash ();
}
@@ -172,8 +172,14 @@ struct hb_serialize_context_t
};
snapshot_t snapshot ()
- { return snapshot_t {
- head, tail, current, current->real_links.length, current->virtual_links.length, errors }; }
+ {
+ return snapshot_t {
+ head, tail, current,
+ current ? current->real_links.length : 0,
+ current ? current->virtual_links.length : 0,
+ errors
+ };
+ }
hb_serialize_context_t (void *start_, unsigned int size) :
start ((char *) start_),
@@ -261,6 +267,7 @@ struct hb_serialize_context_t
/* To be called around main operation. */
template <typename Type>
+ __attribute__((returns_nonnull))
Type *start_serialize ()
{
DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
@@ -303,6 +310,7 @@ struct hb_serialize_context_t
}
template <typename Type = void>
+ __attribute__((returns_nonnull))
Type *push ()
{
if (unlikely (in_error ())) return start_embed<Type> ();
@@ -323,6 +331,8 @@ struct hb_serialize_context_t
{
object_t *obj = current;
if (unlikely (!obj)) return;
+ // Allow cleanup when we've error'd out on int overflows which don't compromise
+ // the serializer state.
if (unlikely (in_error() && !only_overflow ())) return;
current = current->next;
@@ -340,7 +350,9 @@ struct hb_serialize_context_t
{
object_t *obj = current;
if (unlikely (!obj)) return 0;
- if (unlikely (in_error())) return 0;
+ // Allow cleanup when we've error'd out on int overflows which don't compromise
+ // the serializer state.
+ if (unlikely (in_error() && !only_overflow ())) return 0;
current = current->next;
obj->tail = head;
@@ -405,8 +417,11 @@ struct hb_serialize_context_t
// Overflows that happened after the snapshot will be erased by the revert.
if (unlikely (in_error () && !only_overflow ())) return;
assert (snap.current == current);
- current->real_links.shrink (snap.num_real_links);
- current->virtual_links.shrink (snap.num_virtual_links);
+ if (current)
+ {
+ current->real_links.shrink (snap.num_real_links);
+ current->virtual_links.shrink (snap.num_virtual_links);
+ }
errors = snap.errors;
revert (snap.head, snap.tail);
}
@@ -563,13 +578,15 @@ struct hb_serialize_context_t
{
unsigned int l = length () % alignment;
if (l)
- allocate_size<void> (alignment - l);
+ (void) allocate_size<void> (alignment - l);
}
template <typename Type = void>
+ __attribute__((returns_nonnull))
Type *start_embed (const Type *obj HB_UNUSED = nullptr) const
{ return reinterpret_cast<Type *> (this->head); }
template <typename Type>
+ __attribute__((returns_nonnull))
Type *start_embed (const Type &obj) const
{ return start_embed (std::addressof (obj)); }
@@ -597,6 +614,7 @@ struct hb_serialize_context_t
}
template <typename Type>
+ HB_NODISCARD
Type *allocate_size (size_t size, bool clear = true)
{
if (unlikely (in_error ())) return nullptr;
@@ -618,6 +636,7 @@ struct hb_serialize_context_t
{ return this->allocate_size<Type> (Type::min_size); }
template <typename Type>
+ HB_NODISCARD
Type *embed (const Type *obj)
{
unsigned int size = obj->get_size ();
@@ -627,6 +646,7 @@ struct hb_serialize_context_t
return ret;
}
template <typename Type>
+ HB_NODISCARD
Type *embed (const Type &obj)
{ return embed (std::addressof (obj)); }
char *embed (const char *obj, unsigned size)
diff --git a/thirdparty/harfbuzz/src/hb-set-digest.hh b/thirdparty/harfbuzz/src/hb-set-digest.hh
index dab713729b..5681641baa 100644
--- a/thirdparty/harfbuzz/src/hb-set-digest.hh
+++ b/thirdparty/harfbuzz/src/hb-set-digest.hh
@@ -88,14 +88,19 @@ struct hb_set_digest_bits_pattern_t
bool add_range (hb_codepoint_t a, hb_codepoint_t b)
{
+ if (mask == (mask_t) -1) return false;
if ((b >> shift) - (a >> shift) >= mask_bits - 1)
+ {
mask = (mask_t) -1;
- else {
+ return false;
+ }
+ else
+ {
mask_t ma = mask_for (a);
mask_t mb = mask_for (b);
mask |= mb + (mb - ma) - (mb < ma);
+ return true;
}
- return true;
}
template <typename T>
@@ -154,8 +159,7 @@ struct hb_set_digest_combiner_t
bool add_range (hb_codepoint_t a, hb_codepoint_t b)
{
- return head.add_range (a, b) &&
- tail.add_range (a, b);
+ return (int) head.add_range (a, b) | (int) tail.add_range (a, b);
}
template <typename T>
void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
diff --git a/thirdparty/harfbuzz/src/hb-set.h b/thirdparty/harfbuzz/src/hb-set.h
index de53231030..192abf6f63 100644
--- a/thirdparty/harfbuzz/src/hb-set.h
+++ b/thirdparty/harfbuzz/src/hb-set.h
@@ -43,7 +43,7 @@ HB_BEGIN_DECLS
*
* Since: 0.9.21
*/
-#define HB_SET_VALUE_INVALID ((hb_codepoint_t) -1)
+#define HB_SET_VALUE_INVALID HB_CODEPOINT_INVALID
/**
* hb_set_t:
diff --git a/thirdparty/harfbuzz/src/hb-set.hh b/thirdparty/harfbuzz/src/hb-set.hh
index 604802381b..7d1c941e4d 100644
--- a/thirdparty/harfbuzz/src/hb-set.hh
+++ b/thirdparty/harfbuzz/src/hb-set.hh
@@ -115,7 +115,7 @@ struct hb_sparseset_t
/* Sink interface. */
hb_sparseset_t& operator << (hb_codepoint_t v)
{ add (v); return *this; }
- hb_sparseset_t& operator << (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& range)
+ hb_sparseset_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
@@ -174,7 +174,7 @@ struct hb_set_t : hb_sparseset_t<hb_bit_set_invertible_t>
hb_set_t& operator << (hb_codepoint_t v)
{ sparseset::operator<< (v); return *this; }
- hb_set_t& operator << (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& range)
+ hb_set_t& operator << (const hb_codepoint_pair_t& range)
{ sparseset::operator<< (range); return *this; }
};
diff --git a/thirdparty/harfbuzz/src/hb-shape.cc b/thirdparty/harfbuzz/src/hb-shape.cc
index d9598fc704..844f7b9e80 100644
--- a/thirdparty/harfbuzz/src/hb-shape.cc
+++ b/thirdparty/harfbuzz/src/hb-shape.cc
@@ -220,7 +220,7 @@ reset_buffer (hb_buffer_t *buffer,
assert (buffer->ensure (text.length));
buffer->have_positions = false;
buffer->len = text.length;
- memcpy (buffer->info, text.arrayZ, text.length * sizeof (buffer->info[0]));
+ hb_memcpy (buffer->info, text.arrayZ, text.length * sizeof (buffer->info[0]));
hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
}
diff --git a/thirdparty/harfbuzz/src/hb-shaper-list.hh b/thirdparty/harfbuzz/src/hb-shaper-list.hh
index 4158b7094c..f079caf4d3 100644
--- a/thirdparty/harfbuzz/src/hb-shaper-list.hh
+++ b/thirdparty/harfbuzz/src/hb-shaper-list.hh
@@ -33,6 +33,11 @@
/* v--- Add new shapers in the right place here. */
+#ifdef HAVE_WASM
+/* Only picks up fonts that have a "Wasm" table. */
+HB_SHAPER_IMPLEMENT (wasm)
+#endif
+
#ifdef HAVE_GRAPHITE2
/* Only picks up fonts that have a "Silf" table. */
HB_SHAPER_IMPLEMENT (graphite2)
diff --git a/thirdparty/harfbuzz/src/hb-static.cc b/thirdparty/harfbuzz/src/hb-static.cc
index a1a2522edf..c9bd0a61bf 100644
--- a/thirdparty/harfbuzz/src/hb-static.cc
+++ b/thirdparty/harfbuzz/src/hb-static.cc
@@ -31,6 +31,7 @@
#include "hb-aat-layout-common.hh"
#include "hb-aat-layout-feat-table.hh"
+#include "hb-cff-interp-common.hh"
#include "hb-ot-layout-common.hh"
#include "hb-ot-cmap-table.hh"
#include "OT/Color/COLR/COLR.hh"
@@ -58,6 +59,8 @@ DEFINE_NULL_NAMESPACE_BYTES (AAT, Lookup) = {0xFF,0xFF};
/* hb_map_t */
const hb_codepoint_t minus_1 = -1;
+static const unsigned char static_endchar_str[] = {OpCode_endchar};
+const unsigned char *endchar_str = static_endchar_str;
/* hb_face_t */
diff --git a/thirdparty/harfbuzz/src/hb-subset-accelerator.hh b/thirdparty/harfbuzz/src/hb-subset-accelerator.hh
index bb7c62d064..9258383d28 100644
--- a/thirdparty/harfbuzz/src/hb-subset-accelerator.hh
+++ b/thirdparty/harfbuzz/src/hb-subset-accelerator.hh
@@ -42,7 +42,9 @@ struct cff_subset_accelerator_t;
namespace OT {
struct SubtableUnicodesCache;
-};
+struct cff1_subset_accelerator_t;
+struct cff2_subset_accelerator_t;
+}
struct hb_subset_accelerator_t
{
@@ -51,8 +53,8 @@ struct hb_subset_accelerator_t
return &_hb_subset_accelerator_user_data_key;
}
- static hb_subset_accelerator_t* create(const hb_map_t& unicode_to_gid_,
- const hb_multimap_t gid_to_unicodes_,
+ static hb_subset_accelerator_t* create(hb_face_t *source,
+ const hb_map_t& unicode_to_gid_,
const hb_set_t& unicodes_,
bool has_seac_) {
hb_subset_accelerator_t* accel =
@@ -60,8 +62,8 @@ struct hb_subset_accelerator_t
if (unlikely (!accel)) return accel;
- new (accel) hb_subset_accelerator_t (unicode_to_gid_,
- gid_to_unicodes_,
+ new (accel) hb_subset_accelerator_t (source,
+ unicode_to_gid_,
unicodes_,
has_seac_);
@@ -79,36 +81,36 @@ struct hb_subset_accelerator_t
hb_free (accel);
}
- hb_subset_accelerator_t (const hb_map_t& unicode_to_gid_,
- const hb_multimap_t& gid_to_unicodes_,
+ hb_subset_accelerator_t (hb_face_t *source,
+ const hb_map_t& unicode_to_gid_,
const hb_set_t& unicodes_,
bool has_seac_) :
unicode_to_gid(unicode_to_gid_),
- gid_to_unicodes (gid_to_unicodes_),
unicodes(unicodes_),
cmap_cache(nullptr),
destroy_cmap_cache(nullptr),
has_seac(has_seac_),
- cff_accelerator(nullptr),
- destroy_cff_accelerator(nullptr) {}
-
- ~hb_subset_accelerator_t ()
+ source(hb_face_reference (source))
{
- if (cff_accelerator && destroy_cff_accelerator)
- destroy_cff_accelerator ((void*) cff_accelerator);
-
- if (cmap_cache && destroy_cmap_cache)
- destroy_cmap_cache ((void*) cmap_cache);
+ gid_to_unicodes.alloc (unicode_to_gid.get_population ());
+ for (const auto &_ : unicode_to_gid)
+ {
+ auto unicode = _.first;
+ auto gid = _.second;
+ gid_to_unicodes.add (gid, unicode);
+ }
}
+ HB_INTERNAL ~hb_subset_accelerator_t ();
+
// Generic
mutable hb_mutex_t sanitized_table_cache_lock;
mutable hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_blob_t>> sanitized_table_cache;
- const hb_map_t unicode_to_gid;
- const hb_multimap_t gid_to_unicodes;
- const hb_set_t unicodes;
+ hb_map_t unicode_to_gid;
+ hb_multimap_t gid_to_unicodes;
+ hb_set_t unicodes;
// cmap
const OT::SubtableUnicodesCache* cmap_cache;
@@ -116,8 +118,6 @@ struct hb_subset_accelerator_t
// CFF
bool has_seac;
- const CFF::cff_subset_accelerator_t* cff_accelerator;
- hb_destroy_func_t destroy_cff_accelerator;
// TODO(garretrieger): cumulative glyf checksum map
@@ -128,6 +128,13 @@ struct hb_subset_accelerator_t
unicodes.in_error () ||
sanitized_table_cache.in_error ();
}
+
+ hb_face_t *source;
+#ifndef HB_NO_SUBSET_CFF
+ // These have to be immediately after source:
+ mutable hb_face_lazy_loader_t<OT::cff1_subset_accelerator_t, 1> cff1_accel;
+ mutable hb_face_lazy_loader_t<OT::cff2_subset_accelerator_t, 2> cff2_accel;
+#endif
};
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff-common.cc b/thirdparty/harfbuzz/src/hb-subset-cff-common.cc
index 6e1b6f713d..5e4ea5fe7c 100644
--- a/thirdparty/harfbuzz/src/hb-subset-cff-common.cc
+++ b/thirdparty/harfbuzz/src/hb-subset-cff-common.cc
@@ -68,24 +68,35 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan,
/* use hb_set to determine the subset of font dicts */
hb_set_t set;
hb_codepoint_t prev_fd = CFF_UNDEF_CODE;
- for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++)
+ hb_pair_t<unsigned, hb_codepoint_t> last_range {0, 0};
+ auto it = hb_iter (plan->new_to_old_gid_list);
+ auto _ = *it;
+ for (hb_codepoint_t gid = 0; gid < subset_num_glyphs; gid++)
{
- hb_codepoint_t glyph;
- hb_codepoint_t fd;
- if (!plan->old_gid_for_new_gid (i, &glyph))
+ hb_codepoint_t old_glyph;
+ if (gid == _.first)
+ {
+ old_glyph = _.second;
+ _ = *++it;
+ }
+ else
{
/* fonttools retains FDSelect & font dicts for missing glyphs. do the same */
- glyph = i;
+ old_glyph = gid;
}
- fd = src.get_fd (glyph);
- set.add (fd);
+ if (old_glyph >= last_range.second)
+ last_range = src.get_fd_range (old_glyph);
+ unsigned fd = last_range.first;
if (fd != prev_fd)
{
+ set.add (fd);
num_ranges++;
prev_fd = fd;
- code_pair_t pair = { fd, i };
- fdselect_ranges.push (pair);
+ fdselect_ranges.push (code_pair_t { fd, gid });
+
+ if (gid == old_glyph)
+ gid = hb_min (_.first - 1, last_range.second - 1);
}
}
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff-common.hh b/thirdparty/harfbuzz/src/hb-subset-cff-common.hh
index ff50b0e518..f54792648d 100644
--- a/thirdparty/harfbuzz/src/hb-subset-cff-common.hh
+++ b/thirdparty/harfbuzz/src/hb-subset-cff-common.hh
@@ -480,6 +480,7 @@ struct cff_subset_accelerator_t
const hb_vector_t<parsed_cs_str_vec_t>& parsed_local_subrs) {
cff_subset_accelerator_t* accel =
(cff_subset_accelerator_t*) hb_malloc (sizeof(cff_subset_accelerator_t));
+ if (unlikely (!accel)) return nullptr;
new (accel) cff_subset_accelerator_t (original_blob,
parsed_charstrings,
parsed_global_subrs,
@@ -510,15 +511,21 @@ struct cff_subset_accelerator_t
original_blob = hb_blob_reference (original_blob_);
}
- ~cff_subset_accelerator_t() {
+ ~cff_subset_accelerator_t()
+ {
hb_blob_destroy (original_blob);
- hb_map_destroy (glyph_to_sid_map.get_relaxed ());
+ auto *mapping = glyph_to_sid_map.get_relaxed ();
+ if (mapping)
+ {
+ mapping->~glyph_to_sid_map_t ();
+ hb_free (mapping);
+ }
}
parsed_cs_str_vec_t parsed_charstrings;
parsed_cs_str_vec_t parsed_global_subrs;
hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs;
- mutable hb_atomic_ptr_t<hb_map_t> glyph_to_sid_map = nullptr;
+ mutable hb_atomic_ptr_t<glyph_to_sid_map_t> glyph_to_sid_map;
private:
hb_blob_t* original_blob;
@@ -600,9 +607,8 @@ struct subr_remap_t : hb_inc_bimap_t
* no optimization based on usage counts. fonttools doesn't appear doing that either.
*/
- resize (closure->get_population ());
- hb_codepoint_t old_num = HB_SET_VALUE_INVALID;
- while (hb_set_next (closure, &old_num))
+ alloc (closure->get_population ());
+ for (auto old_num : *closure)
add (old_num);
if (get_population () < 1240)
@@ -672,8 +678,8 @@ struct subr_subsetter_t
{
unsigned fd_count = acc.fdCount;
const cff_subset_accelerator_t* cff_accelerator = nullptr;
- if (plan->accelerator && plan->accelerator->cff_accelerator) {
- cff_accelerator = plan->accelerator->cff_accelerator;
+ if (acc.cff_accelerator) {
+ cff_accelerator = acc.cff_accelerator;
fd_count = cff_accelerator->parsed_local_subrs.length;
}
@@ -709,14 +715,13 @@ struct subr_subsetter_t
}
/* phase 1 & 2 */
- for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
+ for (auto _ : plan->new_to_old_gid_list)
{
- hb_codepoint_t glyph;
- if (!plan->old_gid_for_new_gid (i, &glyph))
- continue;
+ hb_codepoint_t new_glyph = _.first;
+ hb_codepoint_t old_glyph = _.second;
- const hb_ubytes_t str = (*acc.charStrings)[glyph];
- unsigned int fd = acc.fdSelect->get_fd (glyph);
+ const hb_ubytes_t str = (*acc.charStrings)[old_glyph];
+ unsigned int fd = acc.fdSelect->get_fd (old_glyph);
if (unlikely (fd >= acc.fdCount))
return false;
@@ -725,9 +730,9 @@ struct subr_subsetter_t
// parsed string already exists in accelerator, copy it and move
// on.
if (cached_charstrings)
- cached_charstrings[i] = &cff_accelerator->parsed_charstrings[glyph];
+ cached_charstrings[new_glyph] = &cff_accelerator->parsed_charstrings[old_glyph];
else
- parsed_charstrings[i] = cff_accelerator->parsed_charstrings[glyph];
+ parsed_charstrings[new_glyph] = cff_accelerator->parsed_charstrings[old_glyph];
continue;
}
@@ -735,8 +740,8 @@ struct subr_subsetter_t
ENV env (str, acc, fd);
cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp (env);
- parsed_charstrings[i].alloc (str.length);
- subr_subset_param_t param (&parsed_charstrings[i],
+ parsed_charstrings[new_glyph].alloc (str.length);
+ subr_subset_param_t param (&parsed_charstrings[new_glyph],
&parsed_global_subrs_storage,
&parsed_local_subrs_storage[fd],
&closures.global_closure,
@@ -747,12 +752,12 @@ struct subr_subsetter_t
return false;
/* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */
- SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[i]);
+ SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[new_glyph]);
/* mark hint ops and arguments for drop */
if ((plan->flags & HB_SUBSET_FLAGS_NO_HINTING) || plan->inprogress_accelerator)
{
- subr_subset_param_t param (&parsed_charstrings[i],
+ subr_subset_param_t param (&parsed_charstrings[new_glyph],
&parsed_global_subrs_storage,
&parsed_local_subrs_storage[fd],
&closures.global_closure,
@@ -760,11 +765,11 @@ struct subr_subsetter_t
plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
drop_hints_param_t drop;
- if (drop_hints_in_str (parsed_charstrings[i], param, drop))
+ if (drop_hints_in_str (parsed_charstrings[new_glyph], param, drop))
{
- parsed_charstrings[i].set_hint_dropped ();
+ parsed_charstrings[new_glyph].set_hint_dropped ();
if (drop.vsindex_dropped)
- parsed_charstrings[i].set_vsindex_dropped ();
+ parsed_charstrings[new_glyph].set_vsindex_dropped ();
}
}
@@ -774,7 +779,7 @@ struct subr_subsetter_t
* The compacting both saves memory and makes further operations
* faster.
*/
- parsed_charstrings[i].compact ();
+ parsed_charstrings[new_glyph].compact ();
}
/* Since parsed strings were loaded from accelerator, we still need
@@ -797,23 +802,40 @@ struct subr_subsetter_t
bool encode_charstrings (str_buff_vec_t &buffArray, bool encode_prefix = true) const
{
- if (unlikely (!buffArray.resize_exact (plan->num_output_glyphs ())))
+ unsigned num_glyphs = plan->num_output_glyphs ();
+ if (unlikely (!buffArray.resize_exact (num_glyphs)))
return false;
- for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
+ hb_codepoint_t last = 0;
+ for (auto _ : plan->new_to_old_gid_list)
{
- hb_codepoint_t glyph;
- if (!plan->old_gid_for_new_gid (i, &glyph))
- {
- /* add an endchar only charstring for a missing glyph if CFF1 */
- if (endchar_op != OpCode_Invalid) buffArray.arrayZ[i].push (endchar_op);
- continue;
- }
- unsigned int fd = acc.fdSelect->get_fd (glyph);
+ hb_codepoint_t gid = _.first;
+ hb_codepoint_t old_glyph = _.second;
+
+ if (endchar_op != OpCode_Invalid)
+ for (; last < gid; last++)
+ {
+ // Hack to point vector to static string.
+ auto &b = buffArray.arrayZ[last];
+ b.length = 1;
+ b.arrayZ = const_cast<unsigned char *>(endchar_str);
+ }
+
+ last++; // Skip over gid
+ unsigned int fd = acc.fdSelect->get_fd (old_glyph);
if (unlikely (fd >= acc.fdCount))
return false;
- if (unlikely (!encode_str (get_parsed_charstring (i), fd, buffArray.arrayZ[i], encode_prefix)))
+ if (unlikely (!encode_str (get_parsed_charstring (gid), fd, buffArray.arrayZ[gid], encode_prefix)))
return false;
}
+ if (endchar_op != OpCode_Invalid)
+ for (; last < num_glyphs; last++)
+ {
+ // Hack to point vector to static string.
+ auto &b = buffArray.arrayZ[last];
+ b.length = 1;
+ b.arrayZ = const_cast<unsigned char *>(endchar_str);
+ }
+
return true;
}
@@ -980,24 +1002,23 @@ struct subr_subsetter_t
const hb_vector_t<parsed_cs_str_vec_t>& local_subrs)
{
closures.reset ();
- for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
+ for (auto _ : plan->new_to_old_gid_list)
{
- hb_codepoint_t glyph;
- if (!plan->old_gid_for_new_gid (i, &glyph))
- continue;
- unsigned int fd = acc.fdSelect->get_fd (glyph);
+ hb_codepoint_t new_glyph = _.first;
+ hb_codepoint_t old_glyph = _.second;
+ unsigned int fd = acc.fdSelect->get_fd (old_glyph);
if (unlikely (fd >= acc.fdCount))
return false;
// Note: const cast is safe here because the collect_subr_refs_in_str only performs a
// closure and does not modify any of the charstrings.
- subr_subset_param_t param (const_cast<parsed_cs_str_t*> (&get_parsed_charstring (i)),
+ subr_subset_param_t param (const_cast<parsed_cs_str_t*> (&get_parsed_charstring (new_glyph)),
const_cast<parsed_cs_str_vec_t*> (&global_subrs),
const_cast<parsed_cs_str_vec_t*> (&local_subrs[fd]),
&closures.global_closure,
&closures.local_closures[fd],
plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
- collect_subr_refs_in_str (get_parsed_charstring (i), param);
+ collect_subr_refs_in_str (get_parsed_charstring (new_glyph), param);
}
return true;
@@ -1105,14 +1126,11 @@ struct subr_subsetter_t
compact_parsed_subrs ();
- plan->inprogress_accelerator->cff_accelerator =
+ acc.cff_accelerator =
cff_subset_accelerator_t::create(acc.blob,
parsed_charstrings,
parsed_global_subrs_storage,
parsed_local_subrs_storage);
- plan->inprogress_accelerator->destroy_cff_accelerator =
- cff_subset_accelerator_t::destroy;
-
}
const parsed_cs_str_t& get_parsed_charstring (unsigned i) const
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff1.cc b/thirdparty/harfbuzz/src/hb-subset-cff1.cc
index 1d7ed6444a..e80d18d097 100644
--- a/thirdparty/harfbuzz/src/hb-subset-cff1.cc
+++ b/thirdparty/harfbuzz/src/hb-subset-cff1.cc
@@ -32,36 +32,59 @@
#include "hb-ot-cff1-table.hh"
#include "hb-set.h"
#include "hb-bimap.hh"
-#include "hb-subset-cff1.hh"
#include "hb-subset-plan.hh"
#include "hb-subset-cff-common.hh"
#include "hb-cff1-interp-cs.hh"
using namespace CFF;
-struct remap_sid_t : hb_inc_bimap_t
+struct remap_sid_t
{
+ unsigned get_population () const { return vector.length; }
+
+ void alloc (unsigned size)
+ {
+ map.alloc (size);
+ vector.alloc (size, true);
+ }
+
+ bool in_error () const
+ { return map.in_error () || vector.in_error (); }
+
unsigned int add (unsigned int sid)
{
- if ((sid != CFF_UNDEF_SID) && !is_std_std (sid))
- return offset_sid (hb_inc_bimap_t::add (unoffset_sid (sid)));
- else
+ if (is_std_str (sid) || (sid == CFF_UNDEF_SID))
return sid;
+
+ sid = unoffset_sid (sid);
+ unsigned v = next;
+ if (map.set (sid, v, false))
+ {
+ vector.push (sid);
+ next++;
+ }
+ else
+ v = map.get (sid); // already exists
+ return offset_sid (v);
}
unsigned int operator[] (unsigned int sid) const
{
- if (is_std_std (sid) || (sid == CFF_UNDEF_SID))
+ if (is_std_str (sid) || (sid == CFF_UNDEF_SID))
return sid;
- else
- return offset_sid (get (unoffset_sid (sid)));
+
+ return offset_sid (map.get (unoffset_sid (sid)));
}
static const unsigned int num_std_strings = 391;
- static bool is_std_std (unsigned int sid) { return sid < num_std_strings; }
+ static bool is_std_str (unsigned int sid) { return sid < num_std_strings; }
static unsigned int offset_sid (unsigned int sid) { return sid + num_std_strings; }
static unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; }
+ unsigned next = 0;
+
+ hb_map_t map;
+ hb_vector_t<unsigned> vector;
};
struct cff1_sub_table_info_t : cff_sub_table_info_t
@@ -271,16 +294,17 @@ struct range_list_t : hb_vector_t<code_pair_t>
/* replace the first glyph ID in the "glyph" field each range with a nLeft value */
bool complete (unsigned int last_glyph)
{
- bool two_byte = false;
+ hb_codepoint_t all_glyphs = 0;
unsigned count = this->length;
for (unsigned int i = count; i; i--)
{
code_pair_t &pair = arrayZ[i - 1];
unsigned int nLeft = last_glyph - pair.glyph - 1;
- two_byte |= nLeft >= 0x100;
+ all_glyphs |= nLeft;
last_glyph = pair.glyph;
pair.glyph = nLeft;
}
+ bool two_byte = all_glyphs >= 0x100;
return two_byte;
}
};
@@ -391,8 +415,10 @@ struct cff1_subr_subsetter_t : subr_subsetter_t<cff1_subr_subsetter_t, CFF1Subrs
}
};
-struct cff_subset_plan {
- cff_subset_plan ()
+namespace OT {
+struct cff1_subset_plan
+{
+ cff1_subset_plan ()
{
for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
topDictModSIDs[i] = CFF_UNDEF_SID;
@@ -402,7 +428,7 @@ struct cff_subset_plan {
{
const Encoding *encoding = acc.encoding;
unsigned int size0, size1;
- hb_codepoint_t code, last_code = CFF_UNDEF_CODE;
+ unsigned code, last_code = CFF_UNDEF_CODE - 1;
hb_vector_t<hb_codepoint_t> supp_codes;
if (unlikely (!subset_enc_code_ranges.resize (0)))
@@ -413,39 +439,42 @@ struct cff_subset_plan {
supp_codes.init ();
+ code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID};
subset_enc_num_codes = plan->num_output_glyphs () - 1;
unsigned int glyph;
- for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++)
+ auto it = hb_iter (plan->new_to_old_gid_list);
+ if (it->first == 0) it++;
+ auto _ = *it;
+ for (glyph = 1; glyph < num_glyphs; glyph++)
{
- hb_codepoint_t old_glyph;
- if (!plan->old_gid_for_new_gid (glyph, &old_glyph))
+ hb_codepoint_t old_glyph;
+ if (glyph == _.first)
+ {
+ old_glyph = _.second;
+ _ = *++it;
+ }
+ else
{
- /* Retain the code for the old missing glyph ID */
+ /* Retain the SID for the old missing glyph ID */
old_glyph = glyph;
}
- code = acc.glyph_to_code (old_glyph);
+ code = acc.glyph_to_code (old_glyph, &glyph_to_sid_cache);
if (code == CFF_UNDEF_CODE)
{
subset_enc_num_codes = glyph - 1;
break;
}
- if ((last_code == CFF_UNDEF_CODE) || (code != last_code + 1))
- {
- code_pair_t pair = { code, glyph };
- subset_enc_code_ranges.push (pair);
- }
+ if (code != last_code + 1)
+ subset_enc_code_ranges.push (code_pair_t {code, glyph});
last_code = code;
if (encoding != &Null (Encoding))
{
- hb_codepoint_t sid = acc.glyph_to_sid (old_glyph);
+ hb_codepoint_t sid = acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache);
encoding->get_supplement_codes (sid, supp_codes);
for (unsigned int i = 0; i < supp_codes.length; i++)
- {
- code_pair_t pair = { supp_codes[i], sid };
- subset_enc_supp_codes.push (pair);
- }
+ subset_enc_supp_codes.push (code_pair_t {supp_codes[i], sid});
}
}
supp_codes.fini ();
@@ -462,65 +491,95 @@ struct cff_subset_plan {
subset_enc_format = 1;
}
- void plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
+ bool plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
{
unsigned int size0, size_ranges;
- hb_codepoint_t sid, last_sid = CFF_UNDEF_CODE;
+ unsigned last_sid = CFF_UNDEF_CODE - 1;
if (unlikely (!subset_charset_ranges.resize (0)))
{
plan->check_success (false);
- return;
+ return false;
}
- hb_map_t *glyph_to_sid_map = (plan->accelerator && plan->accelerator->cff_accelerator) ?
- plan->accelerator->cff_accelerator->glyph_to_sid_map :
- nullptr;
+ code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID};
+
+ unsigned num_glyphs = plan->num_output_glyphs ();
+
+ if (unlikely (!subset_charset_ranges.alloc (hb_min (num_glyphs,
+ acc.num_charset_entries))))
+ {
+ plan->check_success (false);
+ return false;
+ }
+
+ glyph_to_sid_map_t *glyph_to_sid_map = acc.cff_accelerator ?
+ acc.cff_accelerator->glyph_to_sid_map.get_acquire () :
+ nullptr;
bool created_map = false;
- if (!glyph_to_sid_map &&
- ((plan->accelerator && plan->accelerator->cff_accelerator) ||
- plan->num_output_glyphs () > plan->source->get_num_glyphs () / 8.))
+ if (!glyph_to_sid_map && acc.cff_accelerator)
{
created_map = true;
glyph_to_sid_map = acc.create_glyph_to_sid_map ();
}
- unsigned int glyph;
- for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++)
+ auto it = hb_iter (plan->new_to_old_gid_list);
+ if (it->first == 0) it++;
+ auto _ = *it;
+ bool not_is_cid = !acc.is_CID ();
+ bool skip = !not_is_cid && glyph_to_sid_map;
+ if (not_is_cid)
+ sidmap.alloc (num_glyphs);
+ for (hb_codepoint_t glyph = 1; glyph < num_glyphs; glyph++)
{
- hb_codepoint_t old_glyph;
- if (!plan->old_gid_for_new_gid (glyph, &old_glyph))
+ hb_codepoint_t old_glyph;
+ if (glyph == _.first)
+ {
+ old_glyph = _.second;
+ _ = *++it;
+ }
+ else
{
/* Retain the SID for the old missing glyph ID */
old_glyph = glyph;
}
- sid = glyph_to_sid_map ? glyph_to_sid_map->get (old_glyph) : acc.glyph_to_sid (old_glyph);
+ unsigned sid = glyph_to_sid_map ?
+ glyph_to_sid_map->arrayZ[old_glyph].code :
+ acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache);
- if (!acc.is_CID ())
+ if (not_is_cid)
sid = sidmap.add (sid);
- if ((last_sid == CFF_UNDEF_CODE) || (sid != last_sid + 1))
+ if (sid != last_sid + 1)
{
- code_pair_t pair = { sid, glyph };
- subset_charset_ranges.push (pair);
+ subset_charset_ranges.push (code_pair_t {sid, glyph});
+
+ if (glyph == old_glyph && skip)
+ {
+ glyph = hb_min (_.first - 1, glyph_to_sid_map->arrayZ[old_glyph].glyph);
+ sid += glyph - old_glyph;
+ }
}
last_sid = sid;
}
if (created_map)
{
- if (!(plan->accelerator && plan->accelerator->cff_accelerator) ||
- !plan->accelerator->cff_accelerator->glyph_to_sid_map.cmpexch (nullptr, glyph_to_sid_map))
- hb_map_destroy (glyph_to_sid_map);
+ if ((!plan->accelerator && acc.cff_accelerator) ||
+ !acc.cff_accelerator->glyph_to_sid_map.cmpexch (nullptr, glyph_to_sid_map))
+ {
+ glyph_to_sid_map->~glyph_to_sid_map_t ();
+ hb_free (glyph_to_sid_map);
+ }
}
- bool two_byte = subset_charset_ranges.complete (glyph);
+ bool two_byte = subset_charset_ranges.complete (num_glyphs);
- size0 = Charset0::min_size + HBUINT16::static_size * (plan->num_output_glyphs () - 1);
+ size0 = Charset0::get_size (plan->num_output_glyphs ());
if (!two_byte)
- size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.length;
+ size_ranges = Charset1::get_size_for_ranges (subset_charset_ranges.length);
else
- size_ranges = Charset2::min_size + Charset2_Range::static_size * subset_charset_ranges.length;
+ size_ranges = Charset2::get_size_for_ranges (subset_charset_ranges.length);
if (size0 < size_ranges)
subset_charset_format = 0;
@@ -528,19 +587,18 @@ struct cff_subset_plan {
subset_charset_format = 1;
else
subset_charset_format = 2;
+
+ return true;
}
bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc)
{
- sidmap.reset ();
-
for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
{
unsigned int sid = acc.topDict.nameSIDs[i];
if (sid != CFF_UNDEF_SID)
{
- (void)sidmap.add (sid);
- topDictModSIDs[i] = sidmap[sid];
+ topDictModSIDs[i] = sidmap.add (sid);
}
}
@@ -564,19 +622,18 @@ struct cff_subset_plan {
drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE;
- /* check whether the subset renumbers any glyph IDs */
- gid_renum = false;
- for (hb_codepoint_t new_glyph = 0; new_glyph < plan->num_output_glyphs (); new_glyph++)
- {
- if (!plan->old_gid_for_new_gid(new_glyph, &old_glyph))
- continue;
- if (new_glyph != old_glyph) {
- gid_renum = true;
- break;
+ subset_charset = !acc.is_predef_charset ();
+ if (!subset_charset)
+ /* check whether the subset renumbers any glyph IDs */
+ for (const auto &_ : plan->new_to_old_gid_list)
+ {
+ if (_.first != _.second)
+ {
+ subset_charset = true;
+ break;
+ }
}
- }
- subset_charset = gid_renum || !acc.is_predef_charset ();
subset_encoding = !acc.is_CID() && !acc.is_predef_encoding ();
/* top dict INDEX */
@@ -618,7 +675,8 @@ struct cff_subset_plan {
if (unlikely (sidmap.get_population () > 0x8000)) /* assumption: a dict won't reference that many strings */
return false;
- if (subset_charset) plan_subset_charset (acc, plan);
+ if (subset_charset && !plan_subset_charset (acc, plan))
+ return false;
topdict_mod.reassignSIDs (sidmap);
}
@@ -682,8 +740,9 @@ struct cff_subset_plan {
;
}
- return ((subset_charstrings.length == plan->num_output_glyphs ())
- && (fontdicts_mod.length == subset_fdcount));
+ return !plan->in_error () &&
+ (subset_charstrings.length == plan->num_output_glyphs ()) &&
+ (fontdicts_mod.length == subset_fdcount);
}
cff1_top_dict_values_mod_t topdict_mod;
@@ -722,24 +781,22 @@ struct cff_subset_plan {
bool desubroutinize = false;
};
+} // namespace OT
-static bool _serialize_cff1 (hb_serialize_context_t *c,
- cff_subset_plan &plan,
- const OT::cff1::accelerator_subset_t &acc,
- unsigned int num_glyphs)
+bool
+OT::cff1::accelerator_subset_t::serialize (hb_serialize_context_t *c,
+ struct OT::cff1_subset_plan &plan) const
{
/* private dicts & local subrs */
- for (int i = (int)acc.privateDicts.length; --i >= 0 ;)
+ for (int i = (int) privateDicts.length; --i >= 0 ;)
{
if (plan.fdmap.has (i))
{
objidx_t subrs_link = 0;
if (plan.subset_localsubrs[i].length > 0)
{
- CFF1Subrs *dest = c->start_embed <CFF1Subrs> ();
- if (unlikely (!dest)) return false;
- c->push ();
- if (likely (dest && dest->serialize (c, plan.subset_localsubrs[i])))
+ auto *dest = c->push <CFF1Subrs> ();
+ if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
subrs_link = c->pop_pack ();
else
{
@@ -748,12 +805,10 @@ static bool _serialize_cff1 (hb_serialize_context_t *c,
}
}
- PrivateDict *pd = c->start_embed<PrivateDict> ();
- if (unlikely (!pd)) return false;
- c->push ();
+ auto *pd = c->push<PrivateDict> ();
cff1_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints);
/* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
- if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link)))
+ if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link)))
{
unsigned fd = plan.fdmap[i];
plan.fontdicts_mod[fd].privateDictInfo.size = c->length ();
@@ -767,21 +822,20 @@ static bool _serialize_cff1 (hb_serialize_context_t *c,
}
}
- if (!acc.is_CID ())
+ if (!is_CID ())
plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo;
/* CharStrings */
{
c->push<CFF1CharStrings> ();
- unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings);
+ unsigned data_size = 0;
+ unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings, &data_size);
if (unlikely (!c->start_zerocopy (total_size)))
return false;
- CFF1CharStrings *cs = c->start_embed<CFF1CharStrings> ();
- if (unlikely (!cs)) return false;
-
- if (likely (cs->serialize (c, plan.subset_charstrings)))
+ auto *cs = c->start_embed<CFF1CharStrings> ();
+ if (likely (cs->serialize (c, plan.subset_charstrings, &data_size)))
plan.info.char_strings_link = c->pop_pack (false);
else
{
@@ -791,11 +845,9 @@ static bool _serialize_cff1 (hb_serialize_context_t *c,
}
/* FDArray (FD Index) */
- if (acc.fdArray != &Null (CFF1FDArray))
+ if (fdArray != &Null (CFF1FDArray))
{
- CFF1FDArray *fda = c->start_embed<CFF1FDArray> ();
- if (unlikely (!fda)) return false;
- c->push ();
+ auto *fda = c->push<CFF1FDArray> ();
cff1_font_dict_op_serializer_t fontSzr;
auto it = + hb_zip (+ hb_iter (plan.fontdicts_mod), + hb_iter (plan.fontdicts_mod));
if (likely (fda->serialize (c, it, fontSzr)))
@@ -808,10 +860,10 @@ static bool _serialize_cff1 (hb_serialize_context_t *c,
}
/* FDSelect */
- if (acc.fdSelect != &Null (CFF1FDSelect))
+ if (fdSelect != &Null (CFF1FDSelect))
{
c->push ();
- if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *acc.fdSelect, acc.fdCount,
+ if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *fdSelect, fdCount,
plan.subset_fdselect_format, plan.info.fd_select.size,
plan.subset_fdselect_ranges)))
plan.info.fd_select.link = c->pop_pack ();
@@ -825,9 +877,7 @@ static bool _serialize_cff1 (hb_serialize_context_t *c,
/* Charset */
if (plan.subset_charset)
{
- Charset *dest = c->start_embed<Charset> ();
- if (unlikely (!dest)) return false;
- c->push ();
+ auto *dest = c->push<Charset> ();
if (likely (dest->serialize (c,
plan.subset_charset_format,
plan.num_glyphs,
@@ -843,9 +893,7 @@ static bool _serialize_cff1 (hb_serialize_context_t *c,
/* Encoding */
if (plan.subset_encoding)
{
- Encoding *dest = c->start_embed<Encoding> ();
- if (unlikely (!dest)) return false;
- c->push ();
+ auto *dest = c->push<Encoding> ();
if (likely (dest->serialize (c,
plan.subset_enc_format,
plan.subset_enc_num_codes,
@@ -861,9 +909,7 @@ static bool _serialize_cff1 (hb_serialize_context_t *c,
/* global subrs */
{
- c->push ();
- CFF1Subrs *dest = c->start_embed <CFF1Subrs> ();
- if (unlikely (!dest)) return false;
+ auto *dest = c->push <CFF1Subrs> ();
if (likely (dest->serialize (c, plan.subset_globalsubrs)))
c->pop_pack (false);
else
@@ -875,10 +921,9 @@ static bool _serialize_cff1 (hb_serialize_context_t *c,
/* String INDEX */
{
- CFF1StringIndex *dest = c->start_embed<CFF1StringIndex> ();
- if (unlikely (!dest)) return false;
- c->push ();
- if (likely (dest->serialize (c, *acc.stringIndex, plan.sidmap)))
+ auto *dest = c->push<CFF1StringIndex> ();
+ if (likely (!plan.sidmap.in_error () &&
+ dest->serialize (c, *stringIndex, plan.sidmap.vector)))
c->pop_pack ();
else
{
@@ -898,14 +943,12 @@ static bool _serialize_cff1 (hb_serialize_context_t *c,
cff->offSize = 4; /* unused? */
/* name INDEX */
- if (unlikely (!(*acc.nameIndex).copy (c))) return false;
+ if (unlikely (!c->embed (*nameIndex))) return false;
/* top dict INDEX */
{
/* serialize singleton TopDict */
- TopDict *top = c->start_embed<TopDict> ();
- if (!top) return false;
- c->push ();
+ auto *top = c->push<TopDict> ();
cff1_top_dict_op_serializer_t topSzr;
unsigned top_size = 0;
top_dict_modifiers_t modifier (plan.info, plan.topDictModSIDs);
@@ -920,36 +963,23 @@ static bool _serialize_cff1 (hb_serialize_context_t *c,
return false;
}
/* serialize INDEX header for above */
- CFF1Index *dest = c->start_embed<CFF1Index> ();
- if (!dest) return false;
- return dest->serialize_header (c, hb_iter (hb_array_t<unsigned> (&top_size, 1)));
+ auto *dest = c->start_embed<CFF1Index> ();
+ return dest->serialize_header (c, hb_iter (&top_size, 1), top_size);
}
}
-static bool
-_hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc,
- hb_subset_context_t *c)
+bool
+OT::cff1::accelerator_subset_t::subset (hb_subset_context_t *c) const
{
- cff_subset_plan cff_plan;
+ cff1_subset_plan cff_plan;
- if (unlikely (!cff_plan.create (acc, c->plan)))
+ if (unlikely (!cff_plan.create (*this, c->plan)))
{
DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan.");
return false;
}
- return _serialize_cff1 (c->serializer, cff_plan, acc, c->plan->num_output_glyphs ());
-}
-
-bool
-hb_subset_cff1 (hb_subset_context_t *c)
-{
- OT::cff1::accelerator_subset_t acc;
- acc.init (c->plan->source);
- bool result = likely (acc.is_valid ()) && _hb_subset_cff1 (acc, c);
- acc.fini ();
-
- return result;
+ return serialize (c->serializer, cff_plan);
}
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff2.cc b/thirdparty/harfbuzz/src/hb-subset-cff2.cc
index 8ab4620194..3c52fb9c2c 100644
--- a/thirdparty/harfbuzz/src/hb-subset-cff2.cc
+++ b/thirdparty/harfbuzz/src/hb-subset-cff2.cc
@@ -31,7 +31,6 @@
#include "hb-open-type.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-set.h"
-#include "hb-subset-cff2.hh"
#include "hb-subset-plan.hh"
#include "hb-subset-cff-common.hh"
#include "hb-cff2-interp-cs.hh"
@@ -422,11 +421,17 @@ struct cff2_private_dict_op_serializer_t : op_serializer_t
};
+namespace OT {
struct cff2_subset_plan
{
bool create (const OT::cff2::accelerator_subset_t &acc,
hb_subset_plan_t *plan)
{
+ /* make sure notdef is first */
+ hb_codepoint_t old_glyph;
+ if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false;
+
+ num_glyphs = plan->num_output_glyphs ();
orig_fdcount = acc.fdArray->count;
drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
@@ -489,6 +494,7 @@ struct cff2_subset_plan
cff2_sub_table_info_t info;
+ unsigned int num_glyphs;
unsigned int orig_fdcount = 0;
unsigned int subset_fdcount = 1;
unsigned int subset_fdselect_size = 0;
@@ -505,18 +511,18 @@ struct cff2_subset_plan
bool drop_hints = false;
bool desubroutinize = false;
};
+} // namespace OT
-static bool _serialize_cff2 (hb_serialize_context_t *c,
- cff2_subset_plan &plan,
- const OT::cff2::accelerator_subset_t &acc,
- unsigned int num_glyphs,
- hb_array_t<int> normalized_coords)
+bool
+OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c,
+ struct cff2_subset_plan &plan,
+ hb_array_t<int> normalized_coords) const
{
/* private dicts & local subrs */
hb_vector_t<table_info_t> private_dict_infos;
if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false;
- for (int i = (int)acc.privateDicts.length; --i >= 0 ;)
+ for (int i = (int)privateDicts.length; --i >= 0 ;)
{
if (plan.fdmap.has (i))
{
@@ -524,9 +530,7 @@ static bool _serialize_cff2 (hb_serialize_context_t *c,
if (plan.subset_localsubrs[i].length > 0)
{
- CFF2Subrs *dest = c->start_embed <CFF2Subrs> ();
- if (unlikely (!dest)) return false;
- c->push ();
+ auto *dest = c->push <CFF2Subrs> ();
if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
subrs_link = c->pop_pack (false);
else
@@ -535,12 +539,10 @@ static bool _serialize_cff2 (hb_serialize_context_t *c,
return false;
}
}
- PrivateDict *pd = c->start_embed<PrivateDict> ();
- if (unlikely (!pd)) return false;
- c->push ();
+ auto *pd = c->push<PrivateDict> ();
cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned,
- acc.varStore, normalized_coords);
- if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link)))
+ varStore, normalized_coords);
+ if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link)))
{
unsigned fd = plan.fdmap[i];
private_dict_infos[fd].size = c->length ();
@@ -558,14 +560,13 @@ static bool _serialize_cff2 (hb_serialize_context_t *c,
{
c->push ();
- unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings);
+ unsigned data_size = 0;
+ unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings, &data_size);
if (unlikely (!c->start_zerocopy (total_size)))
return false;
- CFF2CharStrings *cs = c->start_embed<CFF2CharStrings> ();
- if (unlikely (!cs)) return false;
-
- if (likely (cs->serialize (c, plan.subset_charstrings)))
+ auto *cs = c->start_embed<CFF2CharStrings> ();
+ if (likely (cs->serialize (c, plan.subset_charstrings, &data_size)))
plan.info.char_strings_link = c->pop_pack (false);
else
{
@@ -575,10 +576,10 @@ static bool _serialize_cff2 (hb_serialize_context_t *c,
}
/* FDSelect */
- if (acc.fdSelect != &Null (CFF2FDSelect))
+ if (fdSelect != &Null (CFF2FDSelect))
{
c->push ();
- if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect,
+ if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *(const FDSelect *)fdSelect,
plan.orig_fdcount,
plan.subset_fdselect_format, plan.subset_fdselect_size,
plan.subset_fdselect_ranges)))
@@ -592,27 +593,32 @@ static bool _serialize_cff2 (hb_serialize_context_t *c,
/* FDArray (FD Index) */
{
- c->push ();
- CFF2FDArray *fda = c->start_embed<CFF2FDArray> ();
- if (unlikely (!fda)) return false;
+ auto *fda = c->push<CFF2FDArray> ();
cff_font_dict_op_serializer_t fontSzr;
auto it =
- + hb_zip (+ hb_iter (acc.fontDicts)
+ + hb_zip (+ hb_iter (fontDicts)
| hb_filter ([&] (const cff2_font_dict_values_t &_)
- { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }),
+ { return plan.fdmap.has (&_ - &fontDicts[0]); }),
hb_iter (private_dict_infos))
;
- if (unlikely (!fda->serialize (c, it, fontSzr))) return false;
+ if (unlikely (!fda->serialize (c, it, fontSzr)))
+ {
+ c->pop_discard ();
+ return false;
+ }
plan.info.fd_array_link = c->pop_pack (false);
}
/* variation store */
- if (acc.varStore != &Null (CFF2VariationStore) &&
+ if (varStore != &Null (CFF2VariationStore) &&
!plan.pinned)
{
- c->push ();
- CFF2VariationStore *dest = c->start_embed<CFF2VariationStore> ();
- if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false;
+ auto *dest = c->push<CFF2VariationStore> ();
+ if (unlikely (!dest->serialize (c, varStore)))
+ {
+ c->pop_discard ();
+ return false;
+ }
plan.info.var_store_link = c->pop_pack (false);
}
@@ -628,34 +634,25 @@ static bool _serialize_cff2 (hb_serialize_context_t *c,
{
TopDict &dict = cff2 + cff2->topDict;
cff2_top_dict_op_serializer_t topSzr;
- if (unlikely (!dict.serialize (c, acc.topDict, topSzr, plan.info))) return false;
+ if (unlikely (!dict.serialize (c, topDict, topSzr, plan.info))) return false;
cff2->topDictSize = c->head - (const char *)&dict;
}
/* global subrs */
{
- CFF2Subrs *dest = c->start_embed <CFF2Subrs> ();
- if (unlikely (!dest)) return false;
+ auto *dest = c->start_embed <CFF2Subrs> ();
return dest->serialize (c, plan.subset_globalsubrs);
}
}
-static bool
-_hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc,
- hb_subset_context_t *c)
+bool
+OT::cff2::accelerator_subset_t::subset (hb_subset_context_t *c) const
{
cff2_subset_plan cff2_plan;
- if (unlikely (!cff2_plan.create (acc, c->plan))) return false;
- return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs (),
- c->plan->normalized_coords.as_array ());
-}
-
-bool
-hb_subset_cff2 (hb_subset_context_t *c)
-{
- OT::cff2::accelerator_subset_t acc (c->plan->source);
- return acc.is_valid () && _hb_subset_cff2 (acc, c);
+ if (unlikely (!cff2_plan.create (*this, c->plan))) return false;
+ return serialize (c->serializer, cff2_plan,
+ c->plan->normalized_coords.as_array ());
}
#endif
diff --git a/thirdparty/harfbuzz/src/hb-subset-input.cc b/thirdparty/harfbuzz/src/hb-subset-input.cc
index 465af50814..e6b23df704 100644
--- a/thirdparty/harfbuzz/src/hb-subset-input.cc
+++ b/thirdparty/harfbuzz/src/hb-subset-input.cc
@@ -438,7 +438,8 @@ hb_subset_input_pin_axis_to_default (hb_subset_input_t *input,
if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info))
return false;
- return input->axes_location.set (axis_tag, axis_info.default_value);
+ float default_val = axis_info.default_value;
+ return input->axes_location.set (axis_tag, Triple (default_val, default_val, default_val));
}
/**
@@ -468,8 +469,52 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input,
return false;
float val = hb_clamp(axis_value, axis_info.min_value, axis_info.max_value);
- return input->axes_location.set (axis_tag, val);
+ return input->axes_location.set (axis_tag, Triple (val, val, val));
}
+
+#ifdef HB_EXPERIMENTAL_API
+/**
+ * hb_subset_input_set_axis_range: (skip)
+ * @input: a #hb_subset_input_t object.
+ * @face: a #hb_face_t object.
+ * @axis_tag: Tag of the axis
+ * @axis_min_value: Minimum value of the axis variation range to set
+ * @axis_max_value: Maximum value of the axis variation range to set
+ *
+ * Restricting the range of variation on an axis in the given subset input object.
+ * New min/max values will be clamped if they're not within the fvar axis range.
+ * If the fvar axis default value is not within the new range, the new default
+ * value will be changed to the new min or max value, whichever is closer to the fvar
+ * axis default.
+ *
+ * Note: input min value can not be bigger than input max value
+ * Note: currently this API does not support changing axis limits yet.It'd be only
+ * used internally for setting axis limits in the internal data structures
+ *
+ * Return value: `true` if success, `false` otherwise
+ *
+ * XSince: EXPERIMENTAL
+ **/
+HB_EXTERN hb_bool_t
+hb_subset_input_set_axis_range (hb_subset_input_t *input,
+ hb_face_t *face,
+ hb_tag_t axis_tag,
+ float axis_min_value,
+ float axis_max_value)
+{
+ if (axis_min_value > axis_max_value)
+ return false;
+
+ hb_ot_var_axis_info_t axis_info;
+ if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info))
+ return false;
+
+ float new_min_val = hb_clamp(axis_min_value, axis_info.min_value, axis_info.max_value);
+ float new_max_val = hb_clamp(axis_max_value, axis_info.min_value, axis_info.max_value);
+ float new_default_val = hb_clamp(axis_info.default_value, new_min_val, new_max_val);
+ return input->axes_location.set (axis_tag, Triple (new_min_val, new_default_val, new_max_val));
+}
+#endif
#endif
/**
diff --git a/thirdparty/harfbuzz/src/hb-subset-input.hh b/thirdparty/harfbuzz/src/hb-subset-input.hh
index 1970f795b9..6ae311e613 100644
--- a/thirdparty/harfbuzz/src/hb-subset-input.hh
+++ b/thirdparty/harfbuzz/src/hb-subset-input.hh
@@ -35,6 +35,7 @@
#include "hb-set.hh"
#include "hb-cplusplus.hh"
#include "hb-font.hh"
+#include "hb-subset-instancer-solver.hh"
struct hb_ot_name_record_ids_t
{
@@ -118,7 +119,7 @@ struct hb_subset_input_t
// If set loca format will always be the long version.
bool force_long_loca = false;
- hb_hashmap_t<hb_tag_t, float> axes_location;
+ hb_hashmap_t<hb_tag_t, Triple> axes_location;
hb_map_t glyph_map;
#ifdef HB_EXPERIMENTAL_API
hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> name_table_overrides;
diff --git a/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc b/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc
index 7a2735c529..c698d944ce 100644
--- a/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc
+++ b/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc
@@ -22,7 +22,7 @@
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
-#include "hb.hh"
+#include "hb-subset-instancer-solver.hh"
/* This file is a straight port of the following:
*
@@ -35,26 +35,6 @@
constexpr static float EPSILON = 1.f / (1 << 14);
constexpr static float MAX_F2DOT14 = float (0x7FFF) / (1 << 14);
-struct Triple {
-
- Triple () :
- minimum (0.f), middle (0.f), maximum (0.f) {}
-
- Triple (float minimum_, float middle_, float maximum_) :
- minimum (minimum_), middle (middle_), maximum (maximum_) {}
-
- bool operator == (const Triple &o) const
- {
- return minimum == o.minimum &&
- middle == o.middle &&
- maximum == o.maximum;
- }
-
- float minimum;
- float middle;
- float maximum;
-};
-
static inline Triple _reverse_negate(const Triple &v)
{ return {-v.maximum, -v.middle, -v.minimum}; }
@@ -82,10 +62,6 @@ static inline float supportScalar (float coord, const Triple &tent)
return (end - coord) / (end - peak);
}
-
-using result_item_t = hb_pair_t<float, Triple>;
-using result_t = hb_vector_t<result_item_t>;
-
static inline result_t
_solve (Triple tent, Triple axisLimit, bool negative = false)
{
@@ -195,9 +171,9 @@ _solve (Triple tent, Triple axisLimit, bool negative = false)
if (gain > outGain)
{
// Crossing point on the axis.
- float crossing = peak + ((1 - gain) * (upper - peak) / (1 - outGain));
+ float crossing = peak + (1 - gain) * (upper - peak);
- Triple loc{peak, peak, crossing};
+ Triple loc{axisDef, peak, crossing};
float scalar = 1.f;
// The part before the crossing point.
@@ -213,7 +189,7 @@ _solve (Triple tent, Triple axisLimit, bool negative = false)
if (upper >= axisMax)
{
Triple loc {crossing, axisMax, axisMax};
- float scalar = supportScalar (axisMax, tent);
+ float scalar = outGain;
out.push (hb_pair (scalar - gain, loc));
}
@@ -247,89 +223,83 @@ _solve (Triple tent, Triple axisLimit, bool negative = false)
// Eternity justify.
Triple loc2 {upper, axisMax, axisMax};
- float scalar2 = 1.f; // supportScalar({"tag": axisMax}, {"tag": tent})
+ float scalar2 = 0.f;
out.push (hb_pair (scalar1 - gain, loc1));
out.push (hb_pair (scalar2 - gain, loc2));
}
}
- /* Case 3: Outermost limit still fits within F2Dot14 bounds;
- * we keep deltas as is and only scale the axes bounds. Deltas beyond -1.0
- * or +1.0 will never be applied as implementations must clamp to that range.
- *
- * A second tent is needed for cases when gain is positive, though we add it
- * unconditionally and it will be dropped because scalar ends up 0.
- *
- * TODO: See if we can just move upper closer to adjust the slope, instead of
- * second tent.
- *
- * | peak |
- * 1.........|............o...|..................
- * | /x\ |
- * | /xxx\ |
- * | /xxxxx\|
- * | /xxxxxxx+
- * | /xxxxxxxx|\
- * 0---|-----|------oxxxxxxxxx|xo---------------1
- * axisMin | lower | upper
- * | |
- * axisDef axisMax
- */
- else if (axisDef + (axisMax - axisDef) * 2 >= upper)
+ else
{
- if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper)
- {
- // we clamp +2.0 to the max F2Dot14 (~1.99994) for convenience
- upper = axisDef + (axisMax - axisDef) * MAX_F2DOT14;
- assert (peak < upper);
- }
-
// Special-case if peak is at axisMax.
if (axisMax == peak)
upper = peak;
- Triple loc1 {hb_max (axisDef, lower), peak, upper};
- float scalar1 = 1.f;
+ /* Case 3:
+ * we keep deltas as is and only scale the axis upper to achieve
+ * the desired new tent if feasible.
+ *
+ * peak
+ * 1.....................o....................
+ * / \_|
+ * ..................../....+_.........outGain
+ * / | \
+ * gain..............+......|..+_.............
+ * /| | | \
+ * 0---|-----------o | | | o----------1
+ * axisMin lower| | | upper
+ * | | newUpper
+ * axisDef axisMax
+ */
+ float newUpper = peak + (1 - gain) * (upper - peak);
+ // I feel like the first condition is always true because
+ // outGain >= gain.
+ if (axisMax <= newUpper && newUpper <= axisDef + (axisMax - axisDef) * 2)
+ {
+ upper = newUpper;
+ if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper)
+ {
+ // we clamp +2.0 to the max F2Dot14 (~1.99994) for convenience
+ upper = axisDef + (axisMax - axisDef) * MAX_F2DOT14;
+ assert (peak < upper);
+ }
- Triple loc2 {peak, upper, upper};
- float scalar2 = 0.f;
+ Triple loc {hb_max (axisDef, lower), peak, upper};
+ float scalar = 1.f;
- // Don't add a dirac delta!
- if (axisDef < upper)
- out.push (hb_pair (scalar1 - gain, loc1));
- if (peak < upper)
- out.push (hb_pair (scalar2 - gain, loc2));
- }
+ out.push (hb_pair (scalar - gain, loc));
+ }
- /* Case 4: New limit doesn't fit; we need to chop into two tents,
- * because the shape of a triangle with part of one side cut off
- * cannot be represented as a triangle itself.
- *
- * | peak |
- * 1.........|......o.|...................
- * | /x\|
- * | |xxy|\_
- * | /xxxy| \_
- * | |xxxxy| \_
- * | /xxxxy| \_
- * 0---|-----|-oxxxxxx| o----------1
- * axisMin | lower | upper
- * | |
- * axisDef axisMax
- */
- else
- {
- Triple loc1 {hb_max (axisDef, lower), peak, axisMax};
- float scalar1 = 1.f;
+ /* Case 4: New limit doesn't fit; we need to chop into two tents,
+ * because the shape of a triangle with part of one side cut off
+ * cannot be represented as a triangle itself.
+ *
+ * | peak |
+ * 1.........|......o.|....................
+ * ..........|...../x\|.............outGain
+ * | |xxy|\_
+ * | /xxxy| \_
+ * | |xxxxy| \_
+ * | /xxxxy| \_
+ * 0---|-----|-oxxxxxx| o----------1
+ * axisMin | lower | upper
+ * | |
+ * axisDef axisMax
+ */
+ else
+ {
+ Triple loc1 {hb_max (axisDef, lower), peak, axisMax};
+ float scalar1 = 1.f;
- Triple loc2 {peak, axisMax, axisMax};
- float scalar2 = supportScalar (axisMax, tent);
+ Triple loc2 {peak, axisMax, axisMax};
+ float scalar2 = outGain;
- out.push (hb_pair (scalar1 - gain, loc1));
- // Don't add a dirac delta!
- if (peak < axisMax)
- out.push (hb_pair (scalar2 - gain, loc2));
+ out.push (hb_pair (scalar1 - gain, loc1));
+ // Don't add a dirac delta!
+ if (peak < axisMax)
+ out.push (hb_pair (scalar2 - gain, loc2));
+ }
}
/* Now, the negative side
@@ -422,19 +392,6 @@ static inline float normalizeValue (float v, const Triple &triple, bool extrapol
}
}
-/* Given a tuple (lower,peak,upper) "tent" and new axis limits
- * (axisMin,axisDefault,axisMax), solves how to represent the tent
- * under the new axis configuration. All values are in normalized
- * -1,0,+1 coordinate system. Tent values can be outside this range.
- *
- * Return value: a list of tuples. Each tuple is of the form
- * (scalar,tent), where scalar is a multipler to multiply any
- * delta-sets by, and tent is a new tent for that output delta-set.
- * If tent value is Triple{}, that is a special deltaset that should
- * be always-enabled (called "gain").
- */
-HB_INTERNAL result_t rebase_tent (Triple tent, Triple axisLimit);
-
result_t
rebase_tent (Triple tent, Triple axisLimit)
{
@@ -460,5 +417,5 @@ rebase_tent (Triple tent, Triple axisLimit)
Triple{n (t.minimum), n (t.middle), n (t.maximum)}));
}
- return sols;
+ return out;
}
diff --git a/thirdparty/harfbuzz/src/hb-subset-instancer-solver.hh b/thirdparty/harfbuzz/src/hb-subset-instancer-solver.hh
new file mode 100644
index 0000000000..b1f8594937
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-instancer-solver.hh
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2023 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_SUBSET_INSTANCER_SOLVER_HH
+#define HB_SUBSET_INSTANCER_SOLVER_HH
+
+#include "hb.hh"
+
+struct Triple {
+
+ Triple () :
+ minimum (0.f), middle (0.f), maximum (0.f) {}
+
+ Triple (float minimum_, float middle_, float maximum_) :
+ minimum (minimum_), middle (middle_), maximum (maximum_) {}
+
+ bool operator == (const Triple &o) const
+ {
+ return minimum == o.minimum &&
+ middle == o.middle &&
+ maximum == o.maximum;
+ }
+
+ bool operator != (const Triple o) const
+ { return !(*this == o); }
+
+ bool is_point () const
+ { return minimum == middle && middle == maximum; }
+
+ bool contains (float point) const
+ { return minimum <= point && point <= maximum; }
+
+ /* from hb_array_t hash ()*/
+ uint32_t hash () const
+ {
+ uint32_t current = /*cbf29ce4*/0x84222325;
+ current = current ^ hb_hash (minimum);
+ current = current * 16777619;
+
+ current = current ^ hb_hash (middle);
+ current = current * 16777619;
+
+ current = current ^ hb_hash (maximum);
+ current = current * 16777619;
+ return current;
+ }
+
+ float minimum;
+ float middle;
+ float maximum;
+};
+
+using result_item_t = hb_pair_t<float, Triple>;
+using result_t = hb_vector_t<result_item_t>;
+
+/* Given a tuple (lower,peak,upper) "tent" and new axis limits
+ * (axisMin,axisDefault,axisMax), solves how to represent the tent
+ * under the new axis configuration. All values are in normalized
+ * -1,0,+1 coordinate system. Tent values can be outside this range.
+ *
+ * Return value: a list of tuples. Each tuple is of the form
+ * (scalar,tent), where scalar is a multipler to multiply any
+ * delta-sets by, and tent is a new tent for that output delta-set.
+ * If tent value is Triple{}, that is a special deltaset that should
+ * be always-enabled (called "gain").
+ */
+HB_INTERNAL result_t rebase_tent (Triple tent, Triple axisLimit);
+
+#endif /* HB_SUBSET_INSTANCER_SOLVER_HH */
diff --git a/thirdparty/harfbuzz/src/hb-subset-plan-member-list.hh b/thirdparty/harfbuzz/src/hb-subset-plan-member-list.hh
index acf508c32d..be29e67ecb 100644
--- a/thirdparty/harfbuzz/src/hb-subset-plan-member-list.hh
+++ b/thirdparty/harfbuzz/src/hb-subset-plan-member-list.hh
@@ -33,7 +33,9 @@
// For each cp that we'd like to retain maps to the corresponding gid.
HB_SUBSET_PLAN_MEMBER (hb_set_t, unicodes)
-HB_SUBSET_PLAN_MEMBER (hb_sorted_vector_t E(<hb_pair_t<hb_codepoint_t, hb_codepoint_t>>), unicode_to_new_gid_list)
+HB_SUBSET_PLAN_MEMBER (hb_sorted_vector_t<hb_codepoint_pair_t>, unicode_to_new_gid_list)
+
+HB_SUBSET_PLAN_MEMBER (hb_sorted_vector_t<hb_codepoint_pair_t>, new_to_old_gid_list)
// name_ids we would like to retain
HB_SUBSET_PLAN_MEMBER (hb_set_t, name_ids)
@@ -97,12 +99,12 @@ HB_SUBSET_PLAN_MEMBER (hb_vector_t<hb_inc_bimap_t>, gdef_varstore_inner_maps)
HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, hb::unique_ptr<hb_blob_t>>), sanitized_table_cache)
-//normalized axes location map
-HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, int>), axes_location)
+//normalized axes range map
+HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, Triple>), axes_location)
HB_SUBSET_PLAN_MEMBER (hb_vector_t<int>, normalized_coords)
-//user specified axes location map
-HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, float>), user_axes_location)
+//user specified axes range map
+HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, Triple>), user_axes_location)
//retained old axis index -> new axis index mapping in fvar axis array
HB_SUBSET_PLAN_MEMBER (hb_map_t, axes_index_map)
@@ -115,9 +117,9 @@ HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<hb_codepoint_t, hb_pair_t E(<unsi
//vmtx metrics map: new gid->(advance, lsb)
HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<hb_codepoint_t, hb_pair_t E(<unsigned, int>)>), vmtx_map)
//boundsWidth map: new gid->boundsWidth, boundWidth=xMax - xMin
-HB_SUBSET_PLAN_MEMBER (mutable hb_map_t, bounds_width_map)
+HB_SUBSET_PLAN_MEMBER (mutable hb_vector_t<unsigned>, bounds_width_vec)
//boundsHeight map: new gid->boundsHeight, boundsHeight=yMax - yMin
-HB_SUBSET_PLAN_MEMBER (mutable hb_map_t, bounds_height_map)
+HB_SUBSET_PLAN_MEMBER (mutable hb_vector_t<unsigned>, bounds_height_vec)
#ifdef HB_EXPERIMENTAL_API
// name table overrides map: hb_ot_name_record_ids_t-> name string new value or
diff --git a/thirdparty/harfbuzz/src/hb-subset-plan.cc b/thirdparty/harfbuzz/src/hb-subset-plan.cc
index 791f92d02d..9a00de3e60 100644
--- a/thirdparty/harfbuzz/src/hb-subset-plan.cc
+++ b/thirdparty/harfbuzz/src/hb-subset-plan.cc
@@ -48,10 +48,24 @@
using OT::Layout::GSUB;
using OT::Layout::GPOS;
+
+hb_subset_accelerator_t::~hb_subset_accelerator_t ()
+{
+ if (cmap_cache && destroy_cmap_cache)
+ destroy_cmap_cache ((void*) cmap_cache);
+
+#ifndef HB_NO_SUBSET_CFF
+ cff1_accel.fini ();
+ cff2_accel.fini ();
+#endif
+ hb_face_destroy (source);
+}
+
+
typedef hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> script_langsys_map;
#ifndef HB_NO_SUBSET_CFF
static inline bool
-_add_cff_seac_components (const OT::cff1::accelerator_t &cff,
+_add_cff_seac_components (const OT::cff1::accelerator_subset_t &cff,
hb_codepoint_t gid,
hb_set_t *gids_to_retain)
{
@@ -135,7 +149,8 @@ static void _collect_layout_indices (hb_subset_plan_t *plan,
hb_set_t *lookup_indices, /* OUT */
hb_set_t *feature_indices, /* OUT */
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* OUT */
- hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map /* OUT */)
+ hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, /* OUT */
+ bool& insert_catch_all_feature_variation_record)
{
unsigned num_features = table.get_feature_count ();
hb_vector_t<hb_tag_t> features;
@@ -171,8 +186,11 @@ static void _collect_layout_indices (hb_subset_plan_t *plan,
&plan->axes_location,
feature_record_cond_idx_map,
feature_substitutes_map,
+ insert_catch_all_feature_variation_record,
feature_indices,
- true,
+ false,
+ false,
+ false,
0,
&conditionset_map
};
@@ -283,7 +301,8 @@ _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
hb_map_t *features,
script_langsys_map *langsys_map,
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map,
- hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map)
+ hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map,
+ bool& insert_catch_all_feature_variation_record)
{
hb_blob_ptr_t<T> table = plan->source_table<T> ();
hb_tag_t table_tag = table->tableTag;
@@ -293,7 +312,8 @@ _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
&lookup_indices,
&feature_indices,
feature_record_cond_idx_map,
- feature_substitutes_map);
+ feature_substitutes_map,
+ insert_catch_all_feature_variation_record);
if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE))
hb_ot_layout_lookups_substitute_closure (plan->source,
@@ -329,7 +349,7 @@ _generate_varstore_inner_maps (const hb_set_t& varidx_set,
{
if (varidx_set.is_empty () || subtable_count == 0) return;
- inner_maps.resize (subtable_count);
+ if (unlikely (!inner_maps.resize (subtable_count))) return;
for (unsigned idx : varidx_set)
{
uint16_t major = idx >> 16;
@@ -356,7 +376,7 @@ _get_hb_font_with_variations (const hb_subset_plan_t *plan)
{
hb_variation_t var;
var.tag = _.first;
- var.value = _.second;
+ var.value = _.second.middle;
vars.push (var);
}
@@ -541,6 +561,8 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
unicodes->get_population () < cmap_unicodes->get_population () &&
glyphs->get_population () < cmap_unicodes->get_population ())
{
+ plan->codepoint_to_glyph->alloc (unicodes->get_population () + glyphs->get_population ());
+
auto &gid_to_unicodes = plan->accelerator->gid_to_unicodes;
for (hb_codepoint_t gid : *glyphs)
{
@@ -569,6 +591,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
}
else
{
+ plan->codepoint_to_glyph->alloc (cmap_unicodes->get_population ());
for (hb_codepoint_t cp : *cmap_unicodes)
{
hb_codepoint_t gid = (*unicode_glyphid_map)[cp];
@@ -581,9 +604,10 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
}
/* Add gids which where requested, but not mapped in cmap */
+ unsigned num_glyphs = plan->source->get_num_glyphs ();
for (hb_codepoint_t gid : *glyphs)
{
- if (gid >= plan->source->get_num_glyphs ())
+ if (gid >= num_glyphs)
break;
plan->_glyphset_gsub.add (gid);
}
@@ -616,7 +640,9 @@ _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count;
if (unlikely (--operation_count < 0)) return operation_count;
- for (auto &item : glyf.glyph_for_gid (gid).get_composite_iterator ())
+ auto glyph = glyf.glyph_for_gid (gid);
+
+ for (auto &item : glyph.get_composite_iterator ())
operation_count =
_glyf_add_gid_and_children (glyf,
item.get_gid (),
@@ -625,7 +651,7 @@ _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
depth);
#ifndef HB_NO_VAR_COMPOSITES
- for (auto &item : glyf.glyph_for_gid (gid).get_var_composite_iterator ())
+ for (auto &item : glyph.get_var_composite_iterator ())
{
operation_count =
_glyf_add_gid_and_children (glyf,
@@ -648,7 +674,7 @@ _nameid_closure (hb_subset_plan_t* plan,
#endif
#ifndef HB_NO_VAR
if (!plan->all_axes_pinned)
- plan->source->table.fvar->collect_name_ids (&plan->user_axes_location, &plan->name_ids);
+ plan->source->table.fvar->collect_name_ids (&plan->user_axes_location, &plan->axes_old_index_tag_map, &plan->name_ids);
#endif
#ifndef HB_NO_COLOR
if (!drop_tables->has (HB_OT_TAG_CPAL))
@@ -677,7 +703,11 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
{
OT::glyf_accelerator_t glyf (plan->source);
#ifndef HB_NO_SUBSET_CFF
- OT::cff1::accelerator_t cff (plan->source);
+ // Note: we cannot use inprogress_accelerator here, since it has not been
+ // created yet. So in case of preprocessed-face (and otherwise), we do an
+ // extra sanitize pass here, which is not ideal.
+ OT::cff1::accelerator_subset_t stack_cff (plan->accelerator ? nullptr : plan->source);
+ const OT::cff1::accelerator_subset_t *cff (plan->accelerator ? plan->accelerator->cff1_accel.get () : &stack_cff);
#endif
plan->_glyphset_gsub.add (0); // Not-def
@@ -694,7 +724,8 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
&plan->gsub_features,
&plan->gsub_langsys,
&plan->gsub_feature_record_cond_idx_map,
- &plan->gsub_feature_substitutes_map);
+ &plan->gsub_feature_substitutes_map,
+ plan->gsub_insert_catch_all_feature_variation_rec);
if (!drop_tables->has (HB_OT_TAG_GPOS))
_closure_glyphs_lookups_features<GPOS> (
@@ -704,7 +735,8 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
&plan->gpos_features,
&plan->gpos_langsys,
&plan->gpos_feature_record_cond_idx_map,
- &plan->gpos_feature_substitutes_map);
+ &plan->gpos_feature_substitutes_map,
+ plan->gpos_insert_catch_all_feature_variation_rec);
#endif
_remove_invalid_gids (&plan->_glyphset_gsub, plan->source->get_num_glyphs ());
@@ -737,9 +769,9 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
if (!plan->accelerator || plan->accelerator->has_seac)
{
bool has_seac = false;
- if (cff.is_valid ())
+ if (cff->is_valid ())
for (hb_codepoint_t gid : cur_glyphset)
- if (_add_cff_seac_components (cff, gid, &plan->_glyphset))
+ if (_add_cff_seac_components (*cff, gid, &plan->_glyphset))
has_seac = true;
plan->has_seac = has_seac;
}
@@ -747,7 +779,6 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
_remove_invalid_gids (&plan->_glyphset, plan->source->get_num_glyphs ());
-
#ifndef HB_NO_VAR
if (!drop_tables->has (HB_OT_TAG_GDEF))
_collect_layout_variation_indices (plan);
@@ -759,10 +790,10 @@ _create_glyph_map_gsub (const hb_set_t* glyph_set_gsub,
const hb_map_t* glyph_map,
hb_map_t* out)
{
+ out->alloc (glyph_set_gsub->get_population ());
+ hb_iter (glyph_set_gsub)
| hb_map ([&] (hb_codepoint_t gid) {
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid,
- glyph_map->get (gid));
+ return hb_codepoint_pair_t (gid, glyph_map->get (gid));
})
| hb_sink (out)
;
@@ -775,11 +806,13 @@ _create_old_gid_to_new_gid_map (const hb_face_t *face,
const hb_map_t *requested_glyph_map,
hb_map_t *glyph_map, /* OUT */
hb_map_t *reverse_glyph_map, /* OUT */
+ hb_sorted_vector_t<hb_codepoint_pair_t> *new_to_old_gid_list /* OUT */,
unsigned int *num_glyphs /* OUT */)
{
unsigned pop = all_gids_to_retain->get_population ();
- reverse_glyph_map->resize (pop);
- glyph_map->resize (pop);
+ reverse_glyph_map->alloc (pop);
+ glyph_map->alloc (pop);
+ new_to_old_gid_list->alloc (pop);
if (*requested_glyph_map)
{
@@ -803,45 +836,44 @@ _create_old_gid_to_new_gid_map (const hb_face_t *face,
for (auto old_gid : all_gids_to_retain->iter ())
{
if (old_gid == 0) {
- reverse_glyph_map->set(0, 0);
+ new_to_old_gid_list->push (hb_pair<hb_codepoint_t, hb_codepoint_t> (0u, 0u));
continue;
}
hb_codepoint_t* new_gid;
if (!requested_glyph_map->has (old_gid, &new_gid))
{
- remaining.add(old_gid);
+ remaining.add(old_gid);
continue;
}
if (*new_gid > max_glyph)
max_glyph = *new_gid;
- reverse_glyph_map->set (*new_gid, old_gid);
+ new_to_old_gid_list->push (hb_pair (*new_gid, old_gid));
}
+ new_to_old_gid_list->qsort ();
// Anything that wasn't mapped by the requested mapping should
// be placed after the requested mapping.
for (auto old_gid : remaining)
- {
- reverse_glyph_map->set(++max_glyph, old_gid);
- }
+ new_to_old_gid_list->push (hb_pair (++max_glyph, old_gid));
*num_glyphs = max_glyph + 1;
}
else if (!retain_gids)
{
+ hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
- | hb_sink (reverse_glyph_map)
+ | hb_sink (new_to_old_gid_list)
;
- *num_glyphs = reverse_glyph_map->get_population ();
+ *num_glyphs = new_to_old_gid_list->length;
}
else
{
+ hb_iter (all_gids_to_retain)
| hb_map ([] (hb_codepoint_t _) {
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, _);
+ return hb_codepoint_pair_t (_, _);
})
- | hb_sink (reverse_glyph_map)
+ | hb_sink (new_to_old_gid_list)
;
hb_codepoint_t max_glyph = HB_SET_VALUE_INVALID;
@@ -850,8 +882,11 @@ _create_old_gid_to_new_gid_map (const hb_face_t *face,
*num_glyphs = max_glyph + 1;
}
- + reverse_glyph_map->iter ()
- | hb_map (&hb_pair_t<hb_codepoint_t, hb_codepoint_t>::reverse)
+ + hb_iter (new_to_old_gid_list)
+ | hb_sink (reverse_glyph_map)
+ ;
+ + hb_iter (new_to_old_gid_list)
+ | hb_map (&hb_codepoint_pair_t::reverse)
| hb_sink (glyph_map)
;
@@ -884,24 +919,35 @@ _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
hb_tag_t axis_tag = axis.get_axis_tag ();
plan->axes_old_index_tag_map.set (old_axis_idx, axis_tag);
- if (!plan->user_axes_location.has (axis_tag))
+ if (!plan->user_axes_location.has (axis_tag) ||
+ !plan->user_axes_location.get (axis_tag).is_point ())
{
axis_not_pinned = true;
plan->axes_index_map.set (old_axis_idx, new_axis_idx);
new_axis_idx++;
}
- else
+
+ if (plan->user_axes_location.has (axis_tag))
{
- int normalized_v = axis.normalize_axis_value (plan->user_axes_location.get (axis_tag));
+ Triple axis_range = plan->user_axes_location.get (axis_tag);
+ int normalized_min = axis.normalize_axis_value (axis_range.minimum);
+ int normalized_default = axis.normalize_axis_value (axis_range.middle);
+ int normalized_max = axis.normalize_axis_value (axis_range.maximum);
+
if (has_avar && old_axis_idx < avar_axis_count)
{
- normalized_v = seg_maps->map (normalized_v);
+ normalized_min = seg_maps->map (normalized_min);
+ normalized_default = seg_maps->map (normalized_default);
+ normalized_max = seg_maps->map (normalized_max);
}
- plan->axes_location.set (axis_tag, normalized_v);
- if (normalized_v != 0)
+ plan->axes_location.set (axis_tag, Triple (static_cast<float> (normalized_min),
+ static_cast<float> (normalized_default),
+ static_cast<float> (normalized_max)));
+
+ if (normalized_default != 0)
plan->pinned_at_default = false;
- plan->normalized_coords[old_axis_idx] = normalized_v;
+ plan->normalized_coords[old_axis_idx] = normalized_default;
}
old_axis_idx++;
@@ -968,7 +1014,7 @@ _update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
continue;
}
plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
- plan->bounds_width_map.set (new_gid, extents.width);
+ plan->bounds_width_vec[new_gid] = extents.width;
}
if (_vmtx.has_data ())
@@ -985,7 +1031,7 @@ _update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
continue;
}
plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
- plan->bounds_height_map.set (new_gid, extents.height);
+ plan->bounds_height_vec[new_gid] = extents.height;
}
}
hb_font_destroy (font);
@@ -1018,6 +1064,8 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
glyph_map = hb_map_create ();
reverse_glyph_map = hb_map_create ();
+ gsub_insert_catch_all_feature_variation_rec = false;
+ gpos_insert_catch_all_feature_variation_rec = false;
gdef_varstore_inner_maps.init ();
user_axes_location = input->axes_location;
@@ -1065,6 +1113,7 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
&input->glyph_map,
glyph_map,
reverse_glyph_map,
+ &new_to_old_gid_list,
&_num_output_glyphs))) {
return;
}
@@ -1082,6 +1131,13 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
glyph_map->get(unicode_to_new_gid_list.arrayZ[i].second);
}
+ bounds_width_vec.resize (_num_output_glyphs, false);
+ for (auto &v : bounds_width_vec)
+ v = 0xFFFFFFFF;
+ bounds_height_vec.resize (_num_output_glyphs, false);
+ for (auto &v : bounds_height_vec)
+ v = 0xFFFFFFFF;
+
if (unlikely (in_error ()))
return;
@@ -1091,19 +1147,9 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
if (attach_accelerator_data)
{
- hb_multimap_t gid_to_unicodes;
-
- hb_map_t &unicode_to_gid = *codepoint_to_glyph;
-
- for (auto unicode : unicodes)
- {
- auto gid = unicode_to_gid[unicode];
- gid_to_unicodes.add (gid, unicode);
- }
-
inprogress_accelerator =
- hb_subset_accelerator_t::create (*codepoint_to_glyph,
- gid_to_unicodes,
+ hb_subset_accelerator_t::create (source,
+ *codepoint_to_glyph,
unicodes,
has_seac);
@@ -1115,6 +1161,29 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
#undef HB_SUBSET_PLAN_MEMBER
}
+hb_subset_plan_t::~hb_subset_plan_t()
+{
+ hb_face_destroy (dest);
+
+ hb_map_destroy (codepoint_to_glyph);
+ hb_map_destroy (glyph_map);
+ hb_map_destroy (reverse_glyph_map);
+#ifndef HB_NO_SUBSET_CFF
+ cff1_accel.fini ();
+ cff2_accel.fini ();
+#endif
+ hb_face_destroy (source);
+
+#ifdef HB_EXPERIMENTAL_API
+ for (auto _ : name_table_overrides.iter_ref ())
+ _.second.fini ();
+#endif
+
+ if (inprogress_accelerator)
+ hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator);
+}
+
+
/**
* hb_subset_plan_create_or_fail:
* @face: font face to create the plan for.
diff --git a/thirdparty/harfbuzz/src/hb-subset-plan.hh b/thirdparty/harfbuzz/src/hb-subset-plan.hh
index 19470ff83e..d156de05d7 100644
--- a/thirdparty/harfbuzz/src/hb-subset-plan.hh
+++ b/thirdparty/harfbuzz/src/hb-subset-plan.hh
@@ -67,28 +67,17 @@ struct head_maxp_info_t
typedef struct head_maxp_info_t head_maxp_info_t;
+namespace OT {
+ struct cff1_subset_accelerator_t;
+ struct cff2_subset_accelerator_t;
+}
+
struct hb_subset_plan_t
{
HB_INTERNAL hb_subset_plan_t (hb_face_t *,
const hb_subset_input_t *input);
- ~hb_subset_plan_t()
- {
- hb_face_destroy (source);
- hb_face_destroy (dest);
-
- hb_map_destroy (codepoint_to_glyph);
- hb_map_destroy (glyph_map);
- hb_map_destroy (reverse_glyph_map);
-
-#ifdef HB_EXPERIMENTAL_API
- for (auto _ : name_table_overrides)
- _.second.fini ();
-#endif
-
- if (inprogress_accelerator)
- hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator);
- }
+ HB_INTERNAL ~hb_subset_plan_t();
hb_object_header_t header;
@@ -106,6 +95,12 @@ struct hb_subset_plan_t
// Plan is only good for a specific source/dest so keep them with it
hb_face_t *source;
+#ifndef HB_NO_SUBSET_CFF
+ // These have to be immediately after source:
+ hb_face_lazy_loader_t<OT::cff1_subset_accelerator_t, 1> cff1_accel;
+ hb_face_lazy_loader_t<OT::cff2_subset_accelerator_t, 2> cff2_accel;
+#endif
+
hb_face_t *dest;
unsigned int _num_output_glyphs;
@@ -114,6 +109,10 @@ struct hb_subset_plan_t
bool pinned_at_default;
bool has_seac;
+ // whether to insert a catch-all FeatureVariationRecord
+ bool gsub_insert_catch_all_feature_variation_rec;
+ bool gpos_insert_catch_all_feature_variation_rec;
+
#define HB_SUBSET_PLAN_MEMBER(Type, Name) Type Name;
#include "hb-subset-plan-member-list.hh"
#undef HB_SUBSET_PLAN_MEMBER
@@ -127,25 +126,31 @@ struct hb_subset_plan_t
public:
template<typename T>
- hb_blob_ptr_t<T> source_table()
+ struct source_table_loader
{
- hb_lock_t lock (accelerator ? &accelerator->sanitized_table_cache_lock : nullptr);
+ hb_blob_ptr_t<T> operator () (hb_subset_plan_t *plan)
+ {
+ hb_lock_t lock (plan->accelerator ? &plan->accelerator->sanitized_table_cache_lock : nullptr);
- auto *cache = accelerator ? &accelerator->sanitized_table_cache : &sanitized_table_cache;
- if (cache
- && !cache->in_error ()
- && cache->has (+T::tableTag)) {
- return hb_blob_reference (cache->get (+T::tableTag).get ());
- }
+ auto *cache = plan->accelerator ? &plan->accelerator->sanitized_table_cache : &plan->sanitized_table_cache;
+ if (cache
+ && !cache->in_error ()
+ && cache->has (+T::tableTag)) {
+ return hb_blob_reference (cache->get (+T::tableTag).get ());
+ }
- hb::unique_ptr<hb_blob_t> table_blob {hb_sanitize_context_t ().reference_table<T> (source)};
- hb_blob_t* ret = hb_blob_reference (table_blob.get ());
+ hb::unique_ptr<hb_blob_t> table_blob {hb_sanitize_context_t ().reference_table<T> (plan->source)};
+ hb_blob_t* ret = hb_blob_reference (table_blob.get ());
- if (likely (cache))
- cache->set (+T::tableTag, std::move (table_blob));
+ if (likely (cache))
+ cache->set (+T::tableTag, std::move (table_blob));
- return ret;
- }
+ return ret;
+ }
+ };
+
+ template<typename T>
+ auto source_table() HB_AUTO_RETURN (source_table_loader<T> {} (this))
bool in_error () const { return !successful; }
@@ -184,15 +189,6 @@ struct hb_subset_plan_t
return _num_output_glyphs;
}
- /*
- * Given an output gid , returns true if that glyph id is an empty
- * glyph (ie. it's a gid that we are dropping all data for).
- */
- inline bool is_empty_glyph (hb_codepoint_t gid) const
- {
- return !_glyphset.has (gid);
- }
-
inline bool new_gid_for_codepoint (hb_codepoint_t codepoint,
hb_codepoint_t *new_gid) const
{
@@ -242,4 +238,5 @@ struct hb_subset_plan_t
}
};
+
#endif /* HB_SUBSET_PLAN_HH */
diff --git a/thirdparty/harfbuzz/src/hb-subset.cc b/thirdparty/harfbuzz/src/hb-subset.cc
index 9c066e6d78..8e8a5eb0bd 100644
--- a/thirdparty/harfbuzz/src/hb-subset.cc
+++ b/thirdparty/harfbuzz/src/hb-subset.cc
@@ -62,6 +62,27 @@
using OT::Layout::GSUB;
using OT::Layout::GPOS;
+
+#ifndef HB_NO_SUBSET_CFF
+template<>
+struct hb_subset_plan_t::source_table_loader<const OT::cff1>
+{
+ auto operator () (hb_subset_plan_t *plan)
+ HB_AUTO_RETURN (plan->accelerator ? plan->accelerator->cff1_accel :
+ plan->inprogress_accelerator ? plan->inprogress_accelerator->cff1_accel :
+ plan->cff1_accel)
+};
+template<>
+struct hb_subset_plan_t::source_table_loader<const OT::cff2>
+{
+ auto operator () (hb_subset_plan_t *plan)
+ HB_AUTO_RETURN (plan->accelerator ? plan->accelerator->cff2_accel :
+ plan->inprogress_accelerator ? plan->inprogress_accelerator->cff2_accel :
+ plan->cff2_accel)
+};
+#endif
+
+
/**
* SECTION:hb-subset
* @title: hb-subset
@@ -192,15 +213,36 @@ _get_table_tags (const hb_subset_plan_t* plan,
static unsigned
_plan_estimate_subset_table_size (hb_subset_plan_t *plan,
unsigned table_len,
- bool same_size)
+ hb_tag_t table_tag)
{
unsigned src_glyphs = plan->source->get_num_glyphs ();
unsigned dst_glyphs = plan->glyphset ()->get_population ();
+ unsigned bulk = 8192;
+ /* Tables that we want to allocate same space as the source table. For GSUB/GPOS it's
+ * because those are expensive to subset, so giving them more room is fine. */
+ bool same_size = table_tag == HB_OT_TAG_GSUB ||
+ table_tag == HB_OT_TAG_GPOS ||
+ table_tag == HB_OT_TAG_name;
+
+ if (plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS)
+ {
+ if (table_tag == HB_OT_TAG_CFF1)
+ {
+ /* Add some extra room for the CFF charset. */
+ bulk += src_glyphs * 16;
+ }
+ else if (table_tag == HB_OT_TAG_CFF2)
+ {
+ /* Just extra CharString offsets. */
+ bulk += src_glyphs * 4;
+ }
+ }
+
if (unlikely (!src_glyphs) || same_size)
- return 512 + table_len;
+ return bulk + table_len;
- return 512 + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs));
+ return bulk + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs));
}
/*
@@ -262,45 +304,46 @@ _try_subset (const TableType *table,
return _try_subset (table, buf, c);
}
+template <typename T>
+static auto _do_destroy (T &t, hb_priority<1>) HB_RETURN (void, t.destroy ())
+
+template <typename T>
+static void _do_destroy (T &t, hb_priority<0>) {}
+
template<typename TableType>
static bool
_subset (hb_subset_plan_t *plan, hb_vector_t<char> &buf)
{
- hb_blob_ptr_t<TableType> source_blob = plan->source_table<TableType> ();
- const TableType *table = source_blob.get ();
+ auto &&source_blob = plan->source_table<TableType> ();
+ auto *table = source_blob.get ();
hb_tag_t tag = TableType::tableTag;
- if (!source_blob.get_blob()->data)
+ hb_blob_t *blob = source_blob.get_blob();
+ if (unlikely (!blob || !blob->data))
{
DEBUG_MSG (SUBSET, nullptr,
"OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag));
- source_blob.destroy ();
+ _do_destroy (source_blob, hb_prioritize);
return false;
}
- /* Tables that we want to allocate same space as the source table. For GSUB/GPOS it's
- * because those are expensive to subset, so giving them more room is fine. */
- bool same_size_table = TableType::tableTag == HB_OT_TAG_GSUB ||
- TableType::tableTag == HB_OT_TAG_GPOS ||
- TableType::tableTag == HB_OT_TAG_name;
-
- unsigned buf_size = _plan_estimate_subset_table_size (plan, source_blob.get_length (), same_size_table);
+ unsigned buf_size = _plan_estimate_subset_table_size (plan, blob->length, TableType::tableTag);
DEBUG_MSG (SUBSET, nullptr,
"OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size);
if (unlikely (!buf.alloc (buf_size)))
{
DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size);
- source_blob.destroy ();
+ _do_destroy (source_blob, hb_prioritize);
return false;
}
bool needed = false;
hb_serialize_context_t serializer (buf.arrayZ, buf.allocated);
{
- hb_subset_context_t c (source_blob.get_blob (), plan, &serializer, tag);
+ hb_subset_context_t c (blob, plan, &serializer, tag);
needed = _try_subset (table, &buf, &c);
}
- source_blob.destroy ();
+ _do_destroy (source_blob, hb_prioritize);
if (serializer.in_error () && !serializer.only_offset_overflow ())
{
@@ -587,46 +630,49 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan)
offset += num_tables;
}
- hb_vector_t<char> buf;
- buf.alloc (4096 - 16);
-
-
bool success = true;
- while (!pending_subset_tags.is_empty ())
{
- if (subsetted_tags.in_error ()
- || pending_subset_tags.in_error ()) {
- success = false;
- goto end;
- }
+ // Grouping to deallocate buf before calling hb_face_reference (plan->dest).
- bool made_changes = false;
- for (hb_tag_t tag : pending_subset_tags)
+ hb_vector_t<char> buf;
+ buf.alloc (8192 - 16);
+
+ while (!pending_subset_tags.is_empty ())
{
- if (!_dependencies_satisfied (plan, tag,
- subsetted_tags,
- pending_subset_tags))
- {
- // delayed subsetting for some tables since they might have dependency on other tables
- // in some cases: e.g: during instantiating glyf tables, hmetrics/vmetrics are updated
- // and saved in subset plan, hmtx/vmtx subsetting need to use these updated metrics values
- continue;
+ if (subsetted_tags.in_error ()
+ || pending_subset_tags.in_error ()) {
+ success = false;
+ goto end;
}
- pending_subset_tags.del (tag);
- subsetted_tags.add (tag);
- made_changes = true;
-
- success = _subset_table (plan, buf, tag);
- if (unlikely (!success)) goto end;
- }
+ bool made_changes = false;
+ for (hb_tag_t tag : pending_subset_tags)
+ {
+ if (!_dependencies_satisfied (plan, tag,
+ subsetted_tags,
+ pending_subset_tags))
+ {
+ // delayed subsetting for some tables since they might have dependency on other tables
+ // in some cases: e.g: during instantiating glyf tables, hmetrics/vmetrics are updated
+ // and saved in subset plan, hmtx/vmtx subsetting need to use these updated metrics values
+ continue;
+ }
+
+ pending_subset_tags.del (tag);
+ subsetted_tags.add (tag);
+ made_changes = true;
+
+ success = _subset_table (plan, buf, tag);
+ if (unlikely (!success)) goto end;
+ }
- if (!made_changes)
- {
- DEBUG_MSG (SUBSET, nullptr, "Table dependencies unable to be satisfied. Subset failed.");
- success = false;
- goto end;
+ if (!made_changes)
+ {
+ DEBUG_MSG (SUBSET, nullptr, "Table dependencies unable to be satisfied. Subset failed.");
+ success = false;
+ goto end;
+ }
}
}
diff --git a/thirdparty/harfbuzz/src/hb-subset.h b/thirdparty/harfbuzz/src/hb-subset.h
index 6368ff93f0..93f1f7f10c 100644
--- a/thirdparty/harfbuzz/src/hb-subset.h
+++ b/thirdparty/harfbuzz/src/hb-subset.h
@@ -177,6 +177,13 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input,
#ifdef HB_EXPERIMENTAL_API
HB_EXTERN hb_bool_t
+hb_subset_input_set_axis_range (hb_subset_input_t *input,
+ hb_face_t *face,
+ hb_tag_t axis_tag,
+ float axis_min_value,
+ float axis_max_value);
+
+HB_EXTERN hb_bool_t
hb_subset_input_override_name_table (hb_subset_input_t *input,
hb_ot_name_id_t name_id,
unsigned platform_id,
diff --git a/thirdparty/harfbuzz/src/hb-vector.hh b/thirdparty/harfbuzz/src/hb-vector.hh
index d61ce48c01..23a96d7081 100644
--- a/thirdparty/harfbuzz/src/hb-vector.hh
+++ b/thirdparty/harfbuzz/src/hb-vector.hh
@@ -54,7 +54,7 @@ struct hb_vector_t
hb_vector_t (const Iterable &o) : hb_vector_t ()
{
auto iter = hb_iter (o);
- if (iter.is_random_access_iterator)
+ if (iter.is_random_access_iterator || iter.has_fast_len)
alloc (hb_len (iter), true);
hb_copy (iter, *this);
}
@@ -62,7 +62,19 @@ struct hb_vector_t
{
alloc (o.length, true);
if (unlikely (in_error ())) return;
- copy_vector (o);
+ copy_array (o.as_array ());
+ }
+ hb_vector_t (array_t o) : hb_vector_t ()
+ {
+ alloc (o.length, true);
+ if (unlikely (in_error ())) return;
+ copy_array (o);
+ }
+ hb_vector_t (c_array_t o) : hb_vector_t ()
+ {
+ alloc (o.length, true);
+ if (unlikely (in_error ())) return;
+ copy_array (o);
}
hb_vector_t (hb_vector_t &&o)
{
@@ -74,7 +86,7 @@ struct hb_vector_t
~hb_vector_t () { fini (); }
public:
- int allocated = 0; /* == -1 means allocation failed. */
+ int allocated = 0; /* < 0 means allocation failed. */
unsigned int length = 0;
public:
Type *arrayZ = nullptr;
@@ -90,19 +102,21 @@ struct hb_vector_t
void fini ()
{
- shrink_vector (0);
- hb_free (arrayZ);
+ /* We allow a hack to make the vector point to a foriegn array
+ * by the user. In that case length/arrayZ are non-zero but
+ * allocated is zero. Don't free anything. */
+ if (allocated)
+ {
+ shrink_vector (0);
+ hb_free (arrayZ);
+ }
init ();
}
void reset ()
{
if (unlikely (in_error ()))
- /* Big Hack! We don't know the true allocated size before
- * an allocation failure happened. But we know it was at
- * least as big as length. Restore it to that and continue
- * as if error did not happen. */
- allocated = length;
+ reset_error ();
resize (0);
}
@@ -119,7 +133,7 @@ struct hb_vector_t
alloc (o.length, true);
if (unlikely (in_error ())) return *this;
- copy_vector (o);
+ copy_array (o.as_array ());
return *this;
}
@@ -191,7 +205,7 @@ struct hb_vector_t
Type *push ()
{
if (unlikely (!resize (length + 1)))
- return &Crap (Type);
+ return std::addressof (Crap (Type));
return std::addressof (arrayZ[length - 1]);
}
template <typename T,
@@ -201,7 +215,7 @@ struct hb_vector_t
Type *push (T&& v)
{
Type *p = push ();
- if (p == &Crap (Type))
+ if (p == std::addressof (Crap (Type)))
// If push failed to allocate then don't copy v, since this may cause
// the created copy to leak memory since we won't have stored a
// reference to it.
@@ -214,24 +228,33 @@ struct hb_vector_t
hb_enable_if (std::is_copy_constructible<T2>::value)>
Type *push (T&& v)
{
- if (unlikely (!alloc (length + 1)))
+ if (unlikely ((int) length >= allocated && !alloc (length + 1)))
// If push failed to allocate then don't copy v, since this may cause
// the created copy to leak memory since we won't have stored a
// reference to it.
- return &Crap (Type);
+ return std::addressof (Crap (Type));
/* Emplace. */
- length++;
- Type *p = std::addressof (arrayZ[length - 1]);
+ Type *p = std::addressof (arrayZ[length++]);
return new (p) Type (std::forward<T> (v));
}
bool in_error () const { return allocated < 0; }
+ void set_error ()
+ {
+ assert (allocated >= 0);
+ allocated = -allocated - 1;
+ }
+ void reset_error ()
+ {
+ assert (allocated < 0);
+ allocated = -(allocated + 1);
+ }
template <typename T = Type,
hb_enable_if (hb_is_trivially_copy_assignable(T))>
Type *
- realloc_vector (unsigned new_allocated)
+ realloc_vector (unsigned new_allocated, hb_priority<0>)
{
if (!new_allocated)
{
@@ -243,7 +266,7 @@ struct hb_vector_t
template <typename T = Type,
hb_enable_if (!hb_is_trivially_copy_assignable(T))>
Type *
- realloc_vector (unsigned new_allocated)
+ realloc_vector (unsigned new_allocated, hb_priority<0>)
{
if (!new_allocated)
{
@@ -263,31 +286,52 @@ struct hb_vector_t
}
return new_array;
}
+ /* Specialization for hb_vector_t<hb_{vector,array}_t<U>> to speed up. */
+ template <typename T = Type,
+ hb_enable_if (hb_is_same (T, hb_vector_t<typename T::item_t>) ||
+ hb_is_same (T, hb_array_t <typename T::item_t>))>
+ Type *
+ realloc_vector (unsigned new_allocated, hb_priority<1>)
+ {
+ if (!new_allocated)
+ {
+ hb_free (arrayZ);
+ return nullptr;
+ }
+ return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type));
+ }
template <typename T = Type,
hb_enable_if (hb_is_trivially_constructible(T))>
void
- grow_vector (unsigned size)
+ grow_vector (unsigned size, hb_priority<0>)
{
- memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
+ hb_memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
length = size;
}
template <typename T = Type,
hb_enable_if (!hb_is_trivially_constructible(T))>
void
- grow_vector (unsigned size)
+ grow_vector (unsigned size, hb_priority<0>)
{
- while (length < size)
- {
- length++;
- new (std::addressof (arrayZ[length - 1])) Type ();
- }
+ for (; length < size; length++)
+ new (std::addressof (arrayZ[length])) Type ();
+ }
+ /* Specialization for hb_vector_t<hb_{vector,array}_t<U>> to speed up. */
+ template <typename T = Type,
+ hb_enable_if (hb_is_same (T, hb_vector_t<typename T::item_t>) ||
+ hb_is_same (T, hb_array_t <typename T::item_t>))>
+ void
+ grow_vector (unsigned size, hb_priority<1>)
+ {
+ hb_memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
+ length = size;
}
template <typename T = Type,
hb_enable_if (hb_is_trivially_copyable (T))>
void
- copy_vector (const hb_vector_t &other)
+ copy_array (hb_array_t<const Type> other)
{
length = other.length;
if (!HB_OPTIMIZE_SIZE_VAL && sizeof (T) >= sizeof (long long))
@@ -301,7 +345,7 @@ struct hb_vector_t
hb_enable_if (!hb_is_trivially_copyable (T) &&
std::is_copy_constructible<T>::value)>
void
- copy_vector (const hb_vector_t &other)
+ copy_array (hb_array_t<const Type> other)
{
length = 0;
while (length < other.length)
@@ -316,7 +360,7 @@ struct hb_vector_t
std::is_default_constructible<T>::value &&
std::is_copy_assignable<T>::value)>
void
- copy_vector (const hb_vector_t &other)
+ copy_array (hb_array_t<const Type> other)
{
length = 0;
while (length < other.length)
@@ -330,11 +374,15 @@ struct hb_vector_t
void
shrink_vector (unsigned size)
{
- while ((unsigned) length > size)
+ assert (size <= length);
+ if (!std::is_trivially_destructible<Type>::value)
{
- arrayZ[(unsigned) length - 1].~Type ();
- length--;
+ unsigned count = length - size;
+ Type *p = arrayZ + length - 1;
+ while (count--)
+ p--->~Type ();
}
+ length = size;
}
void
@@ -381,18 +429,18 @@ struct hb_vector_t
if (unlikely (overflows))
{
- allocated = -1;
+ set_error ();
return false;
}
- Type *new_array = realloc_vector (new_allocated);
+ Type *new_array = realloc_vector (new_allocated, hb_prioritize);
if (unlikely (new_allocated && !new_array))
{
if (new_allocated <= (unsigned) allocated)
return true; // shrinking failed; it's okay; happens in our fuzzer
- allocated = -1;
+ set_error ();
return false;
}
@@ -411,7 +459,7 @@ struct hb_vector_t
if (size > length)
{
if (initialize)
- grow_vector (size);
+ grow_vector (size, hb_prioritize);
}
else if (size < length)
{
diff --git a/thirdparty/harfbuzz/src/hb-version.h b/thirdparty/harfbuzz/src/hb-version.h
index 08d1f55a35..9b27acf598 100644
--- a/thirdparty/harfbuzz/src/hb-version.h
+++ b/thirdparty/harfbuzz/src/hb-version.h
@@ -41,13 +41,13 @@ HB_BEGIN_DECLS
*
* The major component of the library version available at compile-time.
*/
-#define HB_VERSION_MAJOR 7
+#define HB_VERSION_MAJOR 8
/**
* HB_VERSION_MINOR:
*
* The minor component of the library version available at compile-time.
*/
-#define HB_VERSION_MINOR 3
+#define HB_VERSION_MINOR 0
/**
* HB_VERSION_MICRO:
*
@@ -60,7 +60,7 @@ HB_BEGIN_DECLS
*
* A string literal containing the library version available at compile-time.
*/
-#define HB_VERSION_STRING "7.3.0"
+#define HB_VERSION_STRING "8.0.0"
/**
* HB_VERSION_ATLEAST:
diff --git a/thirdparty/harfbuzz/src/hb.hh b/thirdparty/harfbuzz/src/hb.hh
index 205f8cf196..49119b8f82 100644
--- a/thirdparty/harfbuzz/src/hb.hh
+++ b/thirdparty/harfbuzz/src/hb.hh
@@ -315,6 +315,14 @@ extern "C" void hb_free_impl(void *ptr);
#define __restrict
#endif
+#ifndef HB_ALWAYS_INLINE
+#if defined(_MSC_VER)
+#define HB_ALWAYS_INLINE __forceinline
+#else
+#define HB_ALWAYS_INLINE __attribute__((always_inline)) inline
+#endif
+#endif
+
/*
* Borrowed from https://bugzilla.mozilla.org/show_bug.cgi?id=1215411
* HB_FALLTHROUGH is an annotation to suppress compiler warnings about switch
diff --git a/thirdparty/icu4c/common/unicode/ures.h b/thirdparty/icu4c/common/unicode/ures.h
index cc25b6e49c..babc01d426 100644
--- a/thirdparty/icu4c/common/unicode/ures.h
+++ b/thirdparty/icu4c/common/unicode/ures.h
@@ -25,6 +25,7 @@
#ifndef URES_H
#define URES_H
+#include "unicode/char16ptr.h"
#include "unicode/utypes.h"
#include "unicode/uloc.h"
@@ -812,7 +813,7 @@ inline UnicodeString
ures_getUnicodeString(const UResourceBundle *resB, UErrorCode* status) {
UnicodeString result;
int32_t len = 0;
- const char16_t *r = ures_getString(resB, &len, status);
+ const char16_t *r = ConstChar16Ptr(ures_getString(resB, &len, status));
if(U_SUCCESS(*status)) {
result.setTo(true, r, len);
} else {
@@ -837,7 +838,7 @@ inline UnicodeString
ures_getNextUnicodeString(UResourceBundle *resB, const char ** key, UErrorCode* status) {
UnicodeString result;
int32_t len = 0;
- const char16_t* r = ures_getNextString(resB, &len, key, status);
+ const char16_t* r = ConstChar16Ptr(ures_getNextString(resB, &len, key, status));
if(U_SUCCESS(*status)) {
result.setTo(true, r, len);
} else {
@@ -859,7 +860,7 @@ inline UnicodeString
ures_getUnicodeStringByIndex(const UResourceBundle *resB, int32_t indexS, UErrorCode* status) {
UnicodeString result;
int32_t len = 0;
- const char16_t* r = ures_getStringByIndex(resB, indexS, &len, status);
+ const char16_t* r = ConstChar16Ptr(ures_getStringByIndex(resB, indexS, &len, status));
if(U_SUCCESS(*status)) {
result.setTo(true, r, len);
} else {
@@ -882,7 +883,7 @@ inline UnicodeString
ures_getUnicodeStringByKey(const UResourceBundle *resB, const char* key, UErrorCode* status) {
UnicodeString result;
int32_t len = 0;
- const char16_t* r = ures_getStringByKey(resB, key, &len, status);
+ const char16_t* r = ConstChar16Ptr(ures_getStringByKey(resB, key, &len, status));
if(U_SUCCESS(*status)) {
result.setTo(true, r, len);
} else {
diff --git a/thirdparty/icu4c/common/unicode/uvernum.h b/thirdparty/icu4c/common/unicode/uvernum.h
index f0fc671b4b..fc784b2492 100644
--- a/thirdparty/icu4c/common/unicode/uvernum.h
+++ b/thirdparty/icu4c/common/unicode/uvernum.h
@@ -59,7 +59,7 @@
* This value will change in the subsequent releases of ICU
* @stable ICU 2.6
*/
-#define U_ICU_VERSION_MINOR_NUM 1
+#define U_ICU_VERSION_MINOR_NUM 2
/** The current ICU patchlevel version as an integer.
* This value will change in the subsequent releases of ICU
@@ -132,7 +132,7 @@
* This value will change in the subsequent releases of ICU
* @stable ICU 2.4
*/
-#define U_ICU_VERSION "73.1"
+#define U_ICU_VERSION "73.2"
/**
* The current ICU library major version number as a string, for library name suffixes.
@@ -151,7 +151,7 @@
/** Data version in ICU4C.
* @internal ICU 4.4 Internal Use Only
**/
-#define U_ICU_DATA_VERSION "73.1"
+#define U_ICU_DATA_VERSION "73.2"
#endif /* U_HIDE_INTERNAL_API */
/*===========================================================================
diff --git a/thirdparty/icu4c/icudt73l.dat b/thirdparty/icu4c/icudt73l.dat
index b8e2cf7567..fc007e5f96 100644
--- a/thirdparty/icu4c/icudt73l.dat
+++ b/thirdparty/icu4c/icudt73l.dat
Binary files differ
diff --git a/thirdparty/minizip/patches/empty-zip-fix.patch b/thirdparty/minizip/patches/empty-zip-fix.patch
new file mode 100644
index 0000000000..7885d4c3a3
--- /dev/null
+++ b/thirdparty/minizip/patches/empty-zip-fix.patch
@@ -0,0 +1,193 @@
+diff --git a/thirdparty/minizip/unzip.c b/thirdparty/minizip/unzip.c
+index e83aff2773..af73f06137 100644
+--- a/thirdparty/minizip/unzip.c
++++ b/thirdparty/minizip/unzip.c
+@@ -408,6 +408,12 @@ extern int ZEXPORT unzStringFileNameCompare (const char* fileName1,
+ #define BUFREADCOMMENT (0x400)
+ #endif
+
++/* GODOT start */
++#ifndef CENTRALDIRINVALID
++#define CENTRALDIRINVALID (0xffffffffffffffff)
++#endif
++/* GODOT end */
++
+ /*
+ Locate the Central directory of a zipfile (at the end, just before
+ the global comment)
+@@ -419,10 +425,14 @@ local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f
+ ZPOS64_T uSizeFile;
+ ZPOS64_T uBackRead;
+ ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
+- ZPOS64_T uPosFound=0;
++ /* GODOT start */
++ ZPOS64_T uPosFound=CENTRALDIRINVALID;
++ /* GODOT end */
+
+ if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+
+ uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
+@@ -432,7 +442,9 @@ local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f
+
+ buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+ if (buf==NULL)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ uBackRead = 4;
+ while (uBackRead<uMaxBack)
+@@ -462,7 +474,9 @@ local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f
+ break;
+ }
+
+- if (uPosFound!=0)
++ /* GODOT start */
++ if (uPosFound!=CENTRALDIRINVALID)
++ /* GODOT end */
+ break;
+ }
+ TRYFREE(buf);
+@@ -485,12 +499,16 @@ local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib
+ ZPOS64_T uSizeFile;
+ ZPOS64_T uBackRead;
+ ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
+- ZPOS64_T uPosFound=0;
++ /* GODOT start */
++ ZPOS64_T uPosFound=CENTRALDIRINVALID;
++ /* GODOT end */
+ uLong uL;
+ ZPOS64_T relativeOffset;
+
+ if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+
+ uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
+@@ -500,7 +518,9 @@ local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib
+
+ buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+ if (buf==NULL)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ uBackRead = 4;
+ while (uBackRead<uMaxBack)
+@@ -530,47 +550,71 @@ local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib
+ break;
+ }
+
+- if (uPosFound!=0)
++ /* GODOT start */
++ if (uPosFound!=CENTRALDIRINVALID)
++ /* GODOT end */
+ break;
+ }
+ TRYFREE(buf);
+- if (uPosFound == 0)
+- return 0;
++ /* GODOT start */
++ if (uPosFound == CENTRALDIRINVALID)
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ /* Zip64 end of central directory locator */
+ if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ /* the signature, already checked */
+ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ /* number of the disk with the start of the zip64 end of central directory */
+ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+ if (uL != 0)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ /* relative offset of the zip64 end of central directory record */
+ if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ /* total number of disks */
+ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+ if (uL != 1)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ /* Goto end of central directory record */
+ if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ /* the signature */
+ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ if (uL != 0x06064b50)
+- return 0;
++ /* GODOT start */
++ return CENTRALDIRINVALID;
++ /* GODOT end */
+
+ return relativeOffset;
+ }
+@@ -625,7 +669,9 @@ local unzFile unzOpenInternal (const void *path,
+ return NULL;
+
+ central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream);
+- if (central_pos)
++ /* GODOT start */
++ if (central_pos != CENTRALDIRINVALID)
++ /* GODOT end */
+ {
+ uLong uS;
+ ZPOS64_T uL64;
+@@ -687,7 +733,9 @@ local unzFile unzOpenInternal (const void *path,
+ else
+ {
+ central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream);
+- if (central_pos==0)
++ /* GODOT start */
++ if (central_pos==CENTRALDIRINVALID)
++ /* GODOT end */
+ err=UNZ_ERRNO;
+
+ us.isZip64 = 0;
diff --git a/thirdparty/minizip/unzip.c b/thirdparty/minizip/unzip.c
index e83aff2773..f72bf3974f 100644
--- a/thirdparty/minizip/unzip.c
+++ b/thirdparty/minizip/unzip.c
@@ -408,6 +408,12 @@ extern int ZEXPORT unzStringFileNameCompare (const char* fileName1,
#define BUFREADCOMMENT (0x400)
#endif
+/* GODOT start */
+#ifndef CENTRALDIRINVALID
+#define CENTRALDIRINVALID (0xffffffffffffffff)
+#endif
+/* GODOT end */
+
/*
Locate the Central directory of a zipfile (at the end, just before
the global comment)
@@ -419,10 +425,14 @@ local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f
ZPOS64_T uSizeFile;
ZPOS64_T uBackRead;
ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
- ZPOS64_T uPosFound=0;
+ /* GODOT start */
+ ZPOS64_T uPosFound=CENTRALDIRINVALID;
+ /* GODOT end */
if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
@@ -432,7 +442,9 @@ local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f
buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
if (buf==NULL)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
uBackRead = 4;
while (uBackRead<uMaxBack)
@@ -462,7 +474,9 @@ local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_f
break;
}
- if (uPosFound!=0)
+ /* GODOT start */
+ if (uPosFound!=CENTRALDIRINVALID)
+ /* GODOT end */
break;
}
TRYFREE(buf);
@@ -485,12 +499,16 @@ local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib
ZPOS64_T uSizeFile;
ZPOS64_T uBackRead;
ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */
- ZPOS64_T uPosFound=0;
+ /* GODOT start */
+ ZPOS64_T uPosFound=CENTRALDIRINVALID;
+ /* GODOT end */
uLong uL;
ZPOS64_T relativeOffset;
if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream);
@@ -500,7 +518,9 @@ local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib
buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
if (buf==NULL)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
uBackRead = 4;
while (uBackRead<uMaxBack)
@@ -530,47 +550,71 @@ local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib
break;
}
- if (uPosFound!=0)
+ /* GODOT start */
+ if (uPosFound!=CENTRALDIRINVALID)
+ /* GODOT end */
break;
}
TRYFREE(buf);
- if (uPosFound == 0)
- return 0;
+ /* GODOT start */
+ if (uPosFound == CENTRALDIRINVALID)
+ return CENTRALDIRINVALID;
+ /* GODOT end */
/* Zip64 end of central directory locator */
if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
/* the signature, already checked */
if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
/* number of the disk with the start of the zip64 end of central directory */
if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
if (uL != 0)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
/* relative offset of the zip64 end of central directory record */
if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
/* total number of disks */
if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
if (uL != 1)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
/* Goto end of central directory record */
if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
/* the signature */
if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
if (uL != 0x06064b50)
- return 0;
+ /* GODOT start */
+ return CENTRALDIRINVALID;
+ /* GODOT end */
return relativeOffset;
}
@@ -625,7 +669,9 @@ local unzFile unzOpenInternal (const void *path,
return NULL;
central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream);
- if (central_pos)
+ /* GODOT start */
+ if (central_pos != CENTRALDIRINVALID)
+ /* GODOT end */
{
uLong uS;
ZPOS64_T uL64;
@@ -687,7 +733,9 @@ local unzFile unzOpenInternal (const void *path,
else
{
central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream);
- if (central_pos==0)
+ /* GODOT start */
+ if (central_pos==CENTRALDIRINVALID)
+ /* GODOT end */
err=UNZ_ERRNO;
us.isZip64 = 0;
diff --git a/thirdparty/openxr/COPYING.adoc b/thirdparty/openxr/COPYING.adoc
index 3a31362acb..473e7fdc5d 100644
--- a/thirdparty/openxr/COPYING.adoc
+++ b/thirdparty/openxr/COPYING.adoc
@@ -1,6 +1,6 @@
= COPYING.adoc for the Khronos Group OpenXR projects
-// Copyright (c) 2020-2022, The Khronos Group Inc.
+// Copyright (c) 2020-2023, The Khronos Group Inc.
//
// SPDX-License-Identifier: CC-BY-4.0
diff --git a/thirdparty/openxr/include/openxr/openxr.h b/thirdparty/openxr/include/openxr/openxr.h
index 3663f9f14d..5d953fb9ba 100644
--- a/thirdparty/openxr/include/openxr/openxr.h
+++ b/thirdparty/openxr/include/openxr/openxr.h
@@ -2,7 +2,7 @@
#define OPENXR_H_ 1
/*
-** Copyright 2017-2022 The Khronos Group Inc.
+** Copyright 2017-2023 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -25,7 +25,7 @@ extern "C" {
((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL))
// OpenXR current version number.
-#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 26)
+#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 28)
#define XR_VERSION_MAJOR(version) (uint16_t)(((uint64_t)(version) >> 48)& 0xffffULL)
#define XR_VERSION_MINOR(version) (uint16_t)(((uint64_t)(version) >> 32) & 0xffffULL)
@@ -74,6 +74,12 @@ extern "C" {
#define XR_MAX_EVENT_DATA_SIZE sizeof(XrEventDataBuffer)
+#define XR_EXTENSION_ENUM_BASE 1000000000
+
+
+#define XR_EXTENSION_ENUM_STRIDE 1000
+
+
#if !defined(XR_MAY_ALIAS)
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4))
#define XR_MAY_ALIAS __attribute__((__may_alias__))
@@ -214,6 +220,15 @@ typedef enum XrResult {
XR_ERROR_MARKER_ID_INVALID_VARJO = -1000124001,
XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT = -1000142001,
XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT = -1000142002,
+ XR_ERROR_SPACE_MAPPING_INSUFFICIENT_FB = -1000169000,
+ XR_ERROR_SPACE_LOCALIZATION_FAILED_FB = -1000169001,
+ XR_ERROR_SPACE_NETWORK_TIMEOUT_FB = -1000169002,
+ XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB = -1000169003,
+ XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB = -1000169004,
+ XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META = -1000266000,
+ XR_ERROR_HINT_ALREADY_SET_QCOM = -1000306000,
+ XR_ERROR_SPACE_NOT_LOCATABLE_EXT = -1000429000,
+ XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT = -1000429001,
XR_RESULT_MAX_ENUM = 0x7FFFFFFF
} XrResult;
@@ -341,6 +356,11 @@ typedef enum XrStructureType {
XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT = 1000066001,
XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB = 1000070000,
XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB = 1000072000,
+ XR_TYPE_BODY_TRACKER_CREATE_INFO_FB = 1000076001,
+ XR_TYPE_BODY_JOINTS_LOCATE_INFO_FB = 1000076002,
+ XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_FB = 1000076004,
+ XR_TYPE_BODY_JOINT_LOCATIONS_FB = 1000076005,
+ XR_TYPE_BODY_SKELETON_FB = 1000076006,
XR_TYPE_INTERACTION_PROFILE_DPAD_BINDING_EXT = 1000078000,
XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE = 1000079000,
XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT = 1000080000,
@@ -421,6 +441,9 @@ typedef enum XrStructureType {
XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_VARJO = 1000124000,
XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO = 1000124001,
XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO = 1000124002,
+ XR_TYPE_FRAME_END_INFO_ML = 1000135000,
+ XR_TYPE_GLOBAL_DIMMER_FRAME_END_INFO_ML = 1000136000,
+ XR_TYPE_COORDINATE_SPACE_CREATE_INFO_ML = 1000137000,
XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT = 1000142000,
XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT = 1000142001,
XR_TYPE_SPACE_QUERY_INFO_FB = 1000156001,
@@ -438,19 +461,64 @@ typedef enum XrStructureType {
XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB = 1000161000,
XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB = 1000162000,
XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB = 1000163000,
+ XR_TYPE_SPACE_SHARE_INFO_FB = 1000169001,
+ XR_TYPE_EVENT_DATA_SPACE_SHARE_COMPLETE_FB = 1000169002,
XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB = 1000171000,
XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB = 1000171001,
+ XR_TYPE_HAPTIC_AMPLITUDE_ENVELOPE_VIBRATION_FB = 1000173001,
XR_TYPE_SEMANTIC_LABELS_FB = 1000175000,
XR_TYPE_ROOM_LAYOUT_FB = 1000175001,
XR_TYPE_BOUNDARY_2D_FB = 1000175002,
+ XR_TYPE_SEMANTIC_LABELS_SUPPORT_INFO_FB = 1000175010,
XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE = 1000196000,
+ XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB = 1000198001,
+ XR_TYPE_SCENE_CAPTURE_REQUEST_INFO_FB = 1000198050,
XR_TYPE_SPACE_CONTAINER_FB = 1000199000,
+ XR_TYPE_FOVEATION_EYE_TRACKED_PROFILE_CREATE_INFO_META = 1000200000,
+ XR_TYPE_FOVEATION_EYE_TRACKED_STATE_META = 1000200001,
+ XR_TYPE_SYSTEM_FOVEATION_EYE_TRACKED_PROPERTIES_META = 1000200002,
+ XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_FB = 1000201004,
+ XR_TYPE_FACE_TRACKER_CREATE_INFO_FB = 1000201005,
+ XR_TYPE_FACE_EXPRESSION_INFO_FB = 1000201002,
+ XR_TYPE_FACE_EXPRESSION_WEIGHTS_FB = 1000201006,
+ XR_TYPE_EYE_TRACKER_CREATE_INFO_FB = 1000202001,
+ XR_TYPE_EYE_GAZES_INFO_FB = 1000202002,
+ XR_TYPE_EYE_GAZES_FB = 1000202003,
+ XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_FB = 1000202004,
XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB = 1000203002,
XR_TYPE_COMPOSITION_LAYER_SETTINGS_FB = 1000204000,
+ XR_TYPE_HAPTIC_PCM_VIBRATION_FB = 1000209001,
+ XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB = 1000209002,
+ XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_FB = 1000212000,
+ XR_TYPE_LOCAL_DIMMING_FRAME_END_INFO_META = 1000216000,
+ XR_TYPE_SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META = 1000219001,
+ XR_TYPE_VIRTUAL_KEYBOARD_CREATE_INFO_META = 1000219002,
+ XR_TYPE_VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META = 1000219003,
+ XR_TYPE_VIRTUAL_KEYBOARD_LOCATION_INFO_META = 1000219004,
+ XR_TYPE_VIRTUAL_KEYBOARD_MODEL_VISIBILITY_SET_INFO_META = 1000219005,
+ XR_TYPE_VIRTUAL_KEYBOARD_ANIMATION_STATE_META = 1000219006,
+ XR_TYPE_VIRTUAL_KEYBOARD_MODEL_ANIMATION_STATES_META = 1000219007,
+ XR_TYPE_VIRTUAL_KEYBOARD_TEXTURE_DATA_META = 1000219009,
+ XR_TYPE_VIRTUAL_KEYBOARD_INPUT_INFO_META = 1000219010,
+ XR_TYPE_VIRTUAL_KEYBOARD_TEXT_CONTEXT_CHANGE_INFO_META = 1000219011,
+ XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_COMMIT_TEXT_META = 1000219014,
+ XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_BACKSPACE_META = 1000219015,
+ XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_ENTER_META = 1000219016,
+ XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_SHOWN_META = 1000219017,
+ XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_HIDDEN_META = 1000219018,
+ XR_TYPE_EXTERNAL_CAMERA_OCULUS = 1000226000,
XR_TYPE_VULKAN_SWAPCHAIN_CREATE_INFO_META = 1000227000,
XR_TYPE_PERFORMANCE_METRICS_STATE_META = 1000232001,
XR_TYPE_PERFORMANCE_METRICS_COUNTER_META = 1000232002,
+ XR_TYPE_SPACE_LIST_SAVE_INFO_FB = 1000238000,
+ XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB = 1000238001,
+ XR_TYPE_SPACE_USER_CREATE_INFO_FB = 1000241001,
XR_TYPE_SYSTEM_HEADSET_ID_PROPERTIES_META = 1000245000,
+ XR_TYPE_SYSTEM_PASSTHROUGH_COLOR_LUT_PROPERTIES_META = 1000266000,
+ XR_TYPE_PASSTHROUGH_COLOR_LUT_CREATE_INFO_META = 1000266001,
+ XR_TYPE_PASSTHROUGH_COLOR_LUT_UPDATE_INFO_META = 1000266002,
+ XR_TYPE_PASSTHROUGH_COLOR_MAP_LUT_META = 1000266100,
+ XR_TYPE_PASSTHROUGH_COLOR_MAP_INTERPOLATED_LUT_META = 1000266101,
XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC = 1000317001,
XR_TYPE_PASSTHROUGH_COLOR_HTC = 1000317002,
XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC = 1000317003,
@@ -459,9 +527,21 @@ typedef enum XrStructureType {
XR_TYPE_FOVEATION_DYNAMIC_MODE_INFO_HTC = 1000318001,
XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC = 1000318002,
XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT = 1000373000,
+ XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX = 1000375000,
+ XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX = 1000375001,
+ XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT = 1000428000,
+ XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT = 1000428001,
+ XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT = 1000429001,
+ XR_TYPE_PLANE_DETECTOR_BEGIN_INFO_EXT = 1000429002,
+ XR_TYPE_PLANE_DETECTOR_GET_INFO_EXT = 1000429003,
+ XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT = 1000429004,
+ XR_TYPE_PLANE_DETECTOR_LOCATION_EXT = 1000429005,
+ XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT = 1000429006,
+ XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT = 1000429007,
XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR,
XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR,
XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR,
+ XR_TYPE_DEVICE_PCM_SAMPLE_RATE_GET_INFO_FB = XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB,
XR_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
} XrStructureType;
@@ -492,6 +572,7 @@ typedef enum XrReferenceSpaceType {
XR_REFERENCE_SPACE_TYPE_STAGE = 3,
XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT = 1000038000,
XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO = 1000121000,
+ XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT = 1000426000,
XR_REFERENCE_SPACE_TYPE_MAX_ENUM = 0x7FFFFFFF
} XrReferenceSpaceType;
@@ -536,6 +617,7 @@ typedef enum XrObjectType {
XR_OBJECT_TYPE_SPATIAL_ANCHOR_MSFT = 1000039000,
XR_OBJECT_TYPE_SPATIAL_GRAPH_NODE_BINDING_MSFT = 1000049000,
XR_OBJECT_TYPE_HAND_TRACKER_EXT = 1000051000,
+ XR_OBJECT_TYPE_BODY_TRACKER_FB = 1000076000,
XR_OBJECT_TYPE_SCENE_OBSERVER_MSFT = 1000097000,
XR_OBJECT_TYPE_SCENE_MSFT = 1000097001,
XR_OBJECT_TYPE_FACIAL_TRACKER_HTC = 1000104000,
@@ -545,7 +627,13 @@ typedef enum XrObjectType {
XR_OBJECT_TYPE_PASSTHROUGH_LAYER_FB = 1000118002,
XR_OBJECT_TYPE_GEOMETRY_INSTANCE_FB = 1000118004,
XR_OBJECT_TYPE_SPATIAL_ANCHOR_STORE_CONNECTION_MSFT = 1000142000,
+ XR_OBJECT_TYPE_FACE_TRACKER_FB = 1000201000,
+ XR_OBJECT_TYPE_EYE_TRACKER_FB = 1000202000,
+ XR_OBJECT_TYPE_VIRTUAL_KEYBOARD_META = 1000219000,
+ XR_OBJECT_TYPE_SPACE_USER_FB = 1000241000,
+ XR_OBJECT_TYPE_PASSTHROUGH_COLOR_LUT_META = 1000266000,
XR_OBJECT_TYPE_PASSTHROUGH_HTC = 1000317000,
+ XR_OBJECT_TYPE_PLANE_DETECTOR_EXT = 1000429000,
XR_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF
} XrObjectType;
typedef XrFlags64 XrInstanceCreateFlags;
@@ -2542,6 +2630,167 @@ typedef struct XrCompositionLayerSecureContentFB {
+#define XR_FB_body_tracking 1
+XR_DEFINE_HANDLE(XrBodyTrackerFB)
+#define XR_FB_body_tracking_SPEC_VERSION 1
+#define XR_FB_BODY_TRACKING_EXTENSION_NAME "XR_FB_body_tracking"
+
+typedef enum XrBodyJointFB {
+ XR_BODY_JOINT_ROOT_FB = 0,
+ XR_BODY_JOINT_HIPS_FB = 1,
+ XR_BODY_JOINT_SPINE_LOWER_FB = 2,
+ XR_BODY_JOINT_SPINE_MIDDLE_FB = 3,
+ XR_BODY_JOINT_SPINE_UPPER_FB = 4,
+ XR_BODY_JOINT_CHEST_FB = 5,
+ XR_BODY_JOINT_NECK_FB = 6,
+ XR_BODY_JOINT_HEAD_FB = 7,
+ XR_BODY_JOINT_LEFT_SHOULDER_FB = 8,
+ XR_BODY_JOINT_LEFT_SCAPULA_FB = 9,
+ XR_BODY_JOINT_LEFT_ARM_UPPER_FB = 10,
+ XR_BODY_JOINT_LEFT_ARM_LOWER_FB = 11,
+ XR_BODY_JOINT_LEFT_HAND_WRIST_TWIST_FB = 12,
+ XR_BODY_JOINT_RIGHT_SHOULDER_FB = 13,
+ XR_BODY_JOINT_RIGHT_SCAPULA_FB = 14,
+ XR_BODY_JOINT_RIGHT_ARM_UPPER_FB = 15,
+ XR_BODY_JOINT_RIGHT_ARM_LOWER_FB = 16,
+ XR_BODY_JOINT_RIGHT_HAND_WRIST_TWIST_FB = 17,
+ XR_BODY_JOINT_LEFT_HAND_PALM_FB = 18,
+ XR_BODY_JOINT_LEFT_HAND_WRIST_FB = 19,
+ XR_BODY_JOINT_LEFT_HAND_THUMB_METACARPAL_FB = 20,
+ XR_BODY_JOINT_LEFT_HAND_THUMB_PROXIMAL_FB = 21,
+ XR_BODY_JOINT_LEFT_HAND_THUMB_DISTAL_FB = 22,
+ XR_BODY_JOINT_LEFT_HAND_THUMB_TIP_FB = 23,
+ XR_BODY_JOINT_LEFT_HAND_INDEX_METACARPAL_FB = 24,
+ XR_BODY_JOINT_LEFT_HAND_INDEX_PROXIMAL_FB = 25,
+ XR_BODY_JOINT_LEFT_HAND_INDEX_INTERMEDIATE_FB = 26,
+ XR_BODY_JOINT_LEFT_HAND_INDEX_DISTAL_FB = 27,
+ XR_BODY_JOINT_LEFT_HAND_INDEX_TIP_FB = 28,
+ XR_BODY_JOINT_LEFT_HAND_MIDDLE_METACARPAL_FB = 29,
+ XR_BODY_JOINT_LEFT_HAND_MIDDLE_PROXIMAL_FB = 30,
+ XR_BODY_JOINT_LEFT_HAND_MIDDLE_INTERMEDIATE_FB = 31,
+ XR_BODY_JOINT_LEFT_HAND_MIDDLE_DISTAL_FB = 32,
+ XR_BODY_JOINT_LEFT_HAND_MIDDLE_TIP_FB = 33,
+ XR_BODY_JOINT_LEFT_HAND_RING_METACARPAL_FB = 34,
+ XR_BODY_JOINT_LEFT_HAND_RING_PROXIMAL_FB = 35,
+ XR_BODY_JOINT_LEFT_HAND_RING_INTERMEDIATE_FB = 36,
+ XR_BODY_JOINT_LEFT_HAND_RING_DISTAL_FB = 37,
+ XR_BODY_JOINT_LEFT_HAND_RING_TIP_FB = 38,
+ XR_BODY_JOINT_LEFT_HAND_LITTLE_METACARPAL_FB = 39,
+ XR_BODY_JOINT_LEFT_HAND_LITTLE_PROXIMAL_FB = 40,
+ XR_BODY_JOINT_LEFT_HAND_LITTLE_INTERMEDIATE_FB = 41,
+ XR_BODY_JOINT_LEFT_HAND_LITTLE_DISTAL_FB = 42,
+ XR_BODY_JOINT_LEFT_HAND_LITTLE_TIP_FB = 43,
+ XR_BODY_JOINT_RIGHT_HAND_PALM_FB = 44,
+ XR_BODY_JOINT_RIGHT_HAND_WRIST_FB = 45,
+ XR_BODY_JOINT_RIGHT_HAND_THUMB_METACARPAL_FB = 46,
+ XR_BODY_JOINT_RIGHT_HAND_THUMB_PROXIMAL_FB = 47,
+ XR_BODY_JOINT_RIGHT_HAND_THUMB_DISTAL_FB = 48,
+ XR_BODY_JOINT_RIGHT_HAND_THUMB_TIP_FB = 49,
+ XR_BODY_JOINT_RIGHT_HAND_INDEX_METACARPAL_FB = 50,
+ XR_BODY_JOINT_RIGHT_HAND_INDEX_PROXIMAL_FB = 51,
+ XR_BODY_JOINT_RIGHT_HAND_INDEX_INTERMEDIATE_FB = 52,
+ XR_BODY_JOINT_RIGHT_HAND_INDEX_DISTAL_FB = 53,
+ XR_BODY_JOINT_RIGHT_HAND_INDEX_TIP_FB = 54,
+ XR_BODY_JOINT_RIGHT_HAND_MIDDLE_METACARPAL_FB = 55,
+ XR_BODY_JOINT_RIGHT_HAND_MIDDLE_PROXIMAL_FB = 56,
+ XR_BODY_JOINT_RIGHT_HAND_MIDDLE_INTERMEDIATE_FB = 57,
+ XR_BODY_JOINT_RIGHT_HAND_MIDDLE_DISTAL_FB = 58,
+ XR_BODY_JOINT_RIGHT_HAND_MIDDLE_TIP_FB = 59,
+ XR_BODY_JOINT_RIGHT_HAND_RING_METACARPAL_FB = 60,
+ XR_BODY_JOINT_RIGHT_HAND_RING_PROXIMAL_FB = 61,
+ XR_BODY_JOINT_RIGHT_HAND_RING_INTERMEDIATE_FB = 62,
+ XR_BODY_JOINT_RIGHT_HAND_RING_DISTAL_FB = 63,
+ XR_BODY_JOINT_RIGHT_HAND_RING_TIP_FB = 64,
+ XR_BODY_JOINT_RIGHT_HAND_LITTLE_METACARPAL_FB = 65,
+ XR_BODY_JOINT_RIGHT_HAND_LITTLE_PROXIMAL_FB = 66,
+ XR_BODY_JOINT_RIGHT_HAND_LITTLE_INTERMEDIATE_FB = 67,
+ XR_BODY_JOINT_RIGHT_HAND_LITTLE_DISTAL_FB = 68,
+ XR_BODY_JOINT_RIGHT_HAND_LITTLE_TIP_FB = 69,
+ XR_BODY_JOINT_COUNT_FB = 70,
+ XR_BODY_JOINT_NONE_FB = -1,
+ XR_BODY_JOINT_MAX_ENUM_FB = 0x7FFFFFFF
+} XrBodyJointFB;
+
+typedef enum XrBodyJointSetFB {
+ XR_BODY_JOINT_SET_DEFAULT_FB = 0,
+ XR_BODY_JOINT_SET_MAX_ENUM_FB = 0x7FFFFFFF
+} XrBodyJointSetFB;
+typedef struct XrBodyJointLocationFB {
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+} XrBodyJointLocationFB;
+
+// XrSystemBodyTrackingPropertiesFB extends XrSystemProperties
+typedef struct XrSystemBodyTrackingPropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsBodyTracking;
+} XrSystemBodyTrackingPropertiesFB;
+
+typedef struct XrBodyTrackerCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBodyJointSetFB bodyJointSet;
+} XrBodyTrackerCreateInfoFB;
+
+typedef struct XrBodySkeletonJointFB {
+ int32_t joint;
+ int32_t parentJoint;
+ XrPosef pose;
+} XrBodySkeletonJointFB;
+
+typedef struct XrBodySkeletonFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t jointCount;
+ XrBodySkeletonJointFB* joints;
+} XrBodySkeletonFB;
+
+typedef struct XrBodyJointsLocateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+} XrBodyJointsLocateInfoFB;
+
+typedef struct XrBodyJointLocationsFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 isActive;
+ float confidence;
+ uint32_t jointCount;
+ XrBodyJointLocationFB* jointLocations;
+ uint32_t skeletonChangedCount;
+ XrTime time;
+} XrBodyJointLocationsFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateBodyTrackerFB)(XrSession session, const XrBodyTrackerCreateInfoFB* createInfo, XrBodyTrackerFB* bodyTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyBodyTrackerFB)(XrBodyTrackerFB bodyTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateBodyJointsFB)(XrBodyTrackerFB bodyTracker, const XrBodyJointsLocateInfoFB* locateInfo, XrBodyJointLocationsFB* locations);
+typedef XrResult (XRAPI_PTR *PFN_xrGetBodySkeletonFB)(XrBodyTrackerFB bodyTracker, XrBodySkeletonFB* skeleton);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateBodyTrackerFB(
+ XrSession session,
+ const XrBodyTrackerCreateInfoFB* createInfo,
+ XrBodyTrackerFB* bodyTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyBodyTrackerFB(
+ XrBodyTrackerFB bodyTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateBodyJointsFB(
+ XrBodyTrackerFB bodyTracker,
+ const XrBodyJointsLocateInfoFB* locateInfo,
+ XrBodyJointLocationsFB* locations);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetBodySkeletonFB(
+ XrBodyTrackerFB bodyTracker,
+ XrBodySkeletonFB* skeleton);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_EXT_dpad_binding 1
#define XR_EXT_dpad_binding_SPEC_VERSION 1
#define XR_EXT_DPAD_BINDING_EXTENSION_NAME "XR_EXT_dpad_binding"
@@ -3325,12 +3574,13 @@ typedef struct XrHandTrackingCapsulesStateFB {
#define XR_FB_spatial_entity 1
XR_DEFINE_ATOM(XrAsyncRequestIdFB)
#define XR_UUID_SIZE_EXT 16
-#define XR_FB_spatial_entity_SPEC_VERSION 1
+#define XR_FB_spatial_entity_SPEC_VERSION 2
#define XR_FB_SPATIAL_ENTITY_EXTENSION_NAME "XR_FB_spatial_entity"
typedef enum XrSpaceComponentTypeFB {
XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB = 0,
XR_SPACE_COMPONENT_TYPE_STORABLE_FB = 1,
+ XR_SPACE_COMPONENT_TYPE_SHARABLE_FB = 2,
XR_SPACE_COMPONENT_TYPE_BOUNDED_2D_FB = 3,
XR_SPACE_COMPONENT_TYPE_BOUNDED_3D_FB = 4,
XR_SPACE_COMPONENT_TYPE_SEMANTIC_LABELS_FB = 5,
@@ -3834,7 +4084,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGeometryInstanceSetTransformFB(
#define XR_NULL_RENDER_MODEL_KEY_FB 0
XR_DEFINE_ATOM(XrRenderModelKeyFB)
-#define XR_FB_render_model_SPEC_VERSION 3
+#define XR_FB_render_model_SPEC_VERSION 4
#define XR_FB_RENDER_MODEL_EXTENSION_NAME "XR_FB_render_model"
#define XR_MAX_RENDER_MODEL_NAME_SIZE_FB 64
typedef XrFlags64 XrRenderModelFlagsFB;
@@ -3913,7 +4163,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrLoadRenderModelFB(
#define XR_VARJO_foveated_rendering 1
-#define XR_VARJO_foveated_rendering_SPEC_VERSION 2
+#define XR_VARJO_foveated_rendering_SPEC_VERSION 3
#define XR_VARJO_FOVEATED_RENDERING_EXTENSION_NAME "XR_VARJO_foveated_rendering"
// XrViewLocateFoveatedRenderingVARJO extends XrViewLocateInfo
typedef struct XrViewLocateFoveatedRenderingVARJO {
@@ -4045,6 +4295,43 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetViewOffsetVARJO(
#define XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_ML_ml2_controller_interaction"
+#define XR_ML_frame_end_info 1
+#define XR_ML_frame_end_info_SPEC_VERSION 1
+#define XR_ML_FRAME_END_INFO_EXTENSION_NAME "XR_ML_frame_end_info"
+typedef XrFlags64 XrFrameEndInfoFlagsML;
+
+// Flag bits for XrFrameEndInfoFlagsML
+static const XrFrameEndInfoFlagsML XR_FRAME_END_INFO_PROTECTED_BIT_ML = 0x00000001;
+static const XrFrameEndInfoFlagsML XR_FRAME_END_INFO_VIGNETTE_BIT_ML = 0x00000002;
+
+// XrFrameEndInfoML extends XrFrameEndInfo
+typedef struct XrFrameEndInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float focusDistance;
+ XrFrameEndInfoFlagsML flags;
+} XrFrameEndInfoML;
+
+
+
+#define XR_ML_global_dimmer 1
+#define XR_ML_global_dimmer_SPEC_VERSION 1
+#define XR_ML_GLOBAL_DIMMER_EXTENSION_NAME "XR_ML_global_dimmer"
+typedef XrFlags64 XrGlobalDimmerFrameEndInfoFlagsML;
+
+// Flag bits for XrGlobalDimmerFrameEndInfoFlagsML
+static const XrGlobalDimmerFrameEndInfoFlagsML XR_GLOBAL_DIMMER_FRAME_END_INFO_ENABLED_BIT_ML = 0x00000001;
+
+// XrGlobalDimmerFrameEndInfoML extends XrFrameEndInfo
+typedef struct XrGlobalDimmerFrameEndInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float dimmerValue;
+ XrGlobalDimmerFrameEndInfoFlagsML flags;
+} XrGlobalDimmerFrameEndInfoML;
+
+
+
#define XR_MSFT_spatial_anchor_persistence 1
XR_DEFINE_HANDLE(XrSpatialAnchorStoreConnectionMSFT)
#define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_MSFT 256
@@ -4161,6 +4448,7 @@ typedef enum XrSpaceQueryActionFB {
typedef enum XrSpaceStorageLocationFB {
XR_SPACE_STORAGE_LOCATION_INVALID_FB = 0,
XR_SPACE_STORAGE_LOCATION_LOCAL_FB = 1,
+ XR_SPACE_STORAGE_LOCATION_CLOUD_FB = 2,
XR_SPACE_STORAGE_LOCATION_MAX_ENUM_FB = 0x7FFFFFFF
} XrSpaceStorageLocationFB;
typedef struct XR_MAY_ALIAS XrSpaceQueryInfoBaseHeaderFB {
@@ -4309,6 +4597,43 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEraseSpaceFB(
#endif /* !XR_NO_PROTOTYPES */
+#define XR_FB_touch_controller_pro 1
+#define XR_FB_touch_controller_pro_SPEC_VERSION 1
+#define XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME "XR_FB_touch_controller_pro"
+
+
+#define XR_FB_spatial_entity_sharing 1
+XR_DEFINE_HANDLE(XrSpaceUserFB)
+#define XR_FB_spatial_entity_sharing_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_SHARING_EXTENSION_NAME "XR_FB_spatial_entity_sharing"
+typedef struct XrSpaceShareInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t spaceCount;
+ XrSpace* spaces;
+ uint32_t userCount;
+ XrSpaceUserFB* users;
+} XrSpaceShareInfoFB;
+
+typedef struct XrEventDataSpaceShareCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSpaceShareCompleteFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrShareSpacesFB)(XrSession session, const XrSpaceShareInfoFB* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrShareSpacesFB(
+ XrSession session,
+ const XrSpaceShareInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_FB_space_warp 1
#define XR_FB_space_warp_SPEC_VERSION 2
#define XR_FB_SPACE_WARP_EXTENSION_NAME "XR_FB_space_warp"
@@ -4341,9 +4666,31 @@ typedef struct XrSystemSpaceWarpPropertiesFB {
+#define XR_FB_haptic_amplitude_envelope 1
+
+#define XR_MAX_HAPTIC_AMPLITUDE_ENVELOPE_SAMPLES_FB 4000u
+
+#define XR_FB_haptic_amplitude_envelope_SPEC_VERSION 1
+#define XR_FB_HAPTIC_AMPLITUDE_ENVELOPE_EXTENSION_NAME "XR_FB_haptic_amplitude_envelope"
+typedef struct XrHapticAmplitudeEnvelopeVibrationFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrDuration duration;
+ uint32_t amplitudeCount;
+ const float* amplitudes;
+} XrHapticAmplitudeEnvelopeVibrationFB;
+
+
+
#define XR_FB_scene 1
-#define XR_FB_scene_SPEC_VERSION 1
+#define XR_FB_scene_SPEC_VERSION 3
#define XR_FB_SCENE_EXTENSION_NAME "XR_FB_scene"
+typedef XrFlags64 XrSemanticLabelsSupportFlagsFB;
+
+// Flag bits for XrSemanticLabelsSupportFlagsFB
+static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_MULTIPLE_SEMANTIC_LABELS_BIT_FB = 0x00000001;
+static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_DESK_TO_TABLE_MIGRATION_BIT_FB = 0x00000002;
+
typedef struct XrExtent3DfFB {
float width;
float height;
@@ -4387,6 +4734,13 @@ typedef struct XrBoundary2DFB {
XrVector2f* vertices;
} XrBoundary2DFB;
+typedef struct XrSemanticLabelsSupportInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSemanticLabelsSupportFlagsFB flags;
+ const char* recognizedLabels;
+} XrSemanticLabelsSupportInfoFB;
+
typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceBoundingBox2DFB)(XrSession session, XrSpace space, XrRect2Df* boundingBox2DOutput);
typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceBoundingBox3DFB)(XrSession session, XrSpace space, XrRect3DfFB* boundingBox3DOutput);
typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceSemanticLabelsFB)(XrSession session, XrSpace space, XrSemanticLabelsFB* semanticLabelsOutput);
@@ -4453,6 +4807,35 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetDigitalLensControlALMALENCE(
#endif /* !XR_NO_PROTOTYPES */
+#define XR_FB_scene_capture 1
+#define XR_FB_scene_capture_SPEC_VERSION 1
+#define XR_FB_SCENE_CAPTURE_EXTENSION_NAME "XR_FB_scene_capture"
+typedef struct XrEventDataSceneCaptureCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSceneCaptureCompleteFB;
+
+typedef struct XrSceneCaptureRequestInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t requestByteCount;
+ const char* request;
+} XrSceneCaptureRequestInfoFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrRequestSceneCaptureFB)(XrSession session, const XrSceneCaptureRequestInfoFB* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestSceneCaptureFB(
+ XrSession session,
+ const XrSceneCaptureRequestInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_FB_spatial_entity_container 1
#define XR_FB_spatial_entity_container_SPEC_VERSION 2
#define XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME "XR_FB_spatial_entity_container"
@@ -4476,6 +4859,260 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceContainerFB(
#endif /* !XR_NO_PROTOTYPES */
+#define XR_META_foveation_eye_tracked 1
+#define XR_FOVEATION_CENTER_SIZE_META 2
+#define XR_META_foveation_eye_tracked_SPEC_VERSION 1
+#define XR_META_FOVEATION_EYE_TRACKED_EXTENSION_NAME "XR_META_foveation_eye_tracked"
+typedef XrFlags64 XrFoveationEyeTrackedProfileCreateFlagsMETA;
+
+// Flag bits for XrFoveationEyeTrackedProfileCreateFlagsMETA
+
+typedef XrFlags64 XrFoveationEyeTrackedStateFlagsMETA;
+
+// Flag bits for XrFoveationEyeTrackedStateFlagsMETA
+static const XrFoveationEyeTrackedStateFlagsMETA XR_FOVEATION_EYE_TRACKED_STATE_VALID_BIT_META = 0x00000001;
+
+// XrFoveationEyeTrackedProfileCreateInfoMETA extends XrFoveationLevelProfileCreateInfoFB
+typedef struct XrFoveationEyeTrackedProfileCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFoveationEyeTrackedProfileCreateFlagsMETA flags;
+} XrFoveationEyeTrackedProfileCreateInfoMETA;
+
+typedef struct XrFoveationEyeTrackedStateMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrVector2f foveationCenter[XR_FOVEATION_CENTER_SIZE_META];
+ XrFoveationEyeTrackedStateFlagsMETA flags;
+} XrFoveationEyeTrackedStateMETA;
+
+// XrSystemFoveationEyeTrackedPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemFoveationEyeTrackedPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsFoveationEyeTracked;
+} XrSystemFoveationEyeTrackedPropertiesMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetFoveationEyeTrackedStateMETA)(XrSession session, XrFoveationEyeTrackedStateMETA* foveationState);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFoveationEyeTrackedStateMETA(
+ XrSession session,
+ XrFoveationEyeTrackedStateMETA* foveationState);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+#define XR_FB_face_tracking 1
+
+#define XR_FACE_EXPRESSSION_SET_DEFAULT_FB XR_FACE_EXPRESSION_SET_DEFAULT_FB
+
+XR_DEFINE_HANDLE(XrFaceTrackerFB)
+#define XR_FB_face_tracking_SPEC_VERSION 1
+#define XR_FB_FACE_TRACKING_EXTENSION_NAME "XR_FB_face_tracking"
+
+typedef enum XrFaceExpressionFB {
+ XR_FACE_EXPRESSION_BROW_LOWERER_L_FB = 0,
+ XR_FACE_EXPRESSION_BROW_LOWERER_R_FB = 1,
+ XR_FACE_EXPRESSION_CHEEK_PUFF_L_FB = 2,
+ XR_FACE_EXPRESSION_CHEEK_PUFF_R_FB = 3,
+ XR_FACE_EXPRESSION_CHEEK_RAISER_L_FB = 4,
+ XR_FACE_EXPRESSION_CHEEK_RAISER_R_FB = 5,
+ XR_FACE_EXPRESSION_CHEEK_SUCK_L_FB = 6,
+ XR_FACE_EXPRESSION_CHEEK_SUCK_R_FB = 7,
+ XR_FACE_EXPRESSION_CHIN_RAISER_B_FB = 8,
+ XR_FACE_EXPRESSION_CHIN_RAISER_T_FB = 9,
+ XR_FACE_EXPRESSION_DIMPLER_L_FB = 10,
+ XR_FACE_EXPRESSION_DIMPLER_R_FB = 11,
+ XR_FACE_EXPRESSION_EYES_CLOSED_L_FB = 12,
+ XR_FACE_EXPRESSION_EYES_CLOSED_R_FB = 13,
+ XR_FACE_EXPRESSION_EYES_LOOK_DOWN_L_FB = 14,
+ XR_FACE_EXPRESSION_EYES_LOOK_DOWN_R_FB = 15,
+ XR_FACE_EXPRESSION_EYES_LOOK_LEFT_L_FB = 16,
+ XR_FACE_EXPRESSION_EYES_LOOK_LEFT_R_FB = 17,
+ XR_FACE_EXPRESSION_EYES_LOOK_RIGHT_L_FB = 18,
+ XR_FACE_EXPRESSION_EYES_LOOK_RIGHT_R_FB = 19,
+ XR_FACE_EXPRESSION_EYES_LOOK_UP_L_FB = 20,
+ XR_FACE_EXPRESSION_EYES_LOOK_UP_R_FB = 21,
+ XR_FACE_EXPRESSION_INNER_BROW_RAISER_L_FB = 22,
+ XR_FACE_EXPRESSION_INNER_BROW_RAISER_R_FB = 23,
+ XR_FACE_EXPRESSION_JAW_DROP_FB = 24,
+ XR_FACE_EXPRESSION_JAW_SIDEWAYS_LEFT_FB = 25,
+ XR_FACE_EXPRESSION_JAW_SIDEWAYS_RIGHT_FB = 26,
+ XR_FACE_EXPRESSION_JAW_THRUST_FB = 27,
+ XR_FACE_EXPRESSION_LID_TIGHTENER_L_FB = 28,
+ XR_FACE_EXPRESSION_LID_TIGHTENER_R_FB = 29,
+ XR_FACE_EXPRESSION_LIP_CORNER_DEPRESSOR_L_FB = 30,
+ XR_FACE_EXPRESSION_LIP_CORNER_DEPRESSOR_R_FB = 31,
+ XR_FACE_EXPRESSION_LIP_CORNER_PULLER_L_FB = 32,
+ XR_FACE_EXPRESSION_LIP_CORNER_PULLER_R_FB = 33,
+ XR_FACE_EXPRESSION_LIP_FUNNELER_LB_FB = 34,
+ XR_FACE_EXPRESSION_LIP_FUNNELER_LT_FB = 35,
+ XR_FACE_EXPRESSION_LIP_FUNNELER_RB_FB = 36,
+ XR_FACE_EXPRESSION_LIP_FUNNELER_RT_FB = 37,
+ XR_FACE_EXPRESSION_LIP_PRESSOR_L_FB = 38,
+ XR_FACE_EXPRESSION_LIP_PRESSOR_R_FB = 39,
+ XR_FACE_EXPRESSION_LIP_PUCKER_L_FB = 40,
+ XR_FACE_EXPRESSION_LIP_PUCKER_R_FB = 41,
+ XR_FACE_EXPRESSION_LIP_STRETCHER_L_FB = 42,
+ XR_FACE_EXPRESSION_LIP_STRETCHER_R_FB = 43,
+ XR_FACE_EXPRESSION_LIP_SUCK_LB_FB = 44,
+ XR_FACE_EXPRESSION_LIP_SUCK_LT_FB = 45,
+ XR_FACE_EXPRESSION_LIP_SUCK_RB_FB = 46,
+ XR_FACE_EXPRESSION_LIP_SUCK_RT_FB = 47,
+ XR_FACE_EXPRESSION_LIP_TIGHTENER_L_FB = 48,
+ XR_FACE_EXPRESSION_LIP_TIGHTENER_R_FB = 49,
+ XR_FACE_EXPRESSION_LIPS_TOWARD_FB = 50,
+ XR_FACE_EXPRESSION_LOWER_LIP_DEPRESSOR_L_FB = 51,
+ XR_FACE_EXPRESSION_LOWER_LIP_DEPRESSOR_R_FB = 52,
+ XR_FACE_EXPRESSION_MOUTH_LEFT_FB = 53,
+ XR_FACE_EXPRESSION_MOUTH_RIGHT_FB = 54,
+ XR_FACE_EXPRESSION_NOSE_WRINKLER_L_FB = 55,
+ XR_FACE_EXPRESSION_NOSE_WRINKLER_R_FB = 56,
+ XR_FACE_EXPRESSION_OUTER_BROW_RAISER_L_FB = 57,
+ XR_FACE_EXPRESSION_OUTER_BROW_RAISER_R_FB = 58,
+ XR_FACE_EXPRESSION_UPPER_LID_RAISER_L_FB = 59,
+ XR_FACE_EXPRESSION_UPPER_LID_RAISER_R_FB = 60,
+ XR_FACE_EXPRESSION_UPPER_LIP_RAISER_L_FB = 61,
+ XR_FACE_EXPRESSION_UPPER_LIP_RAISER_R_FB = 62,
+ XR_FACE_EXPRESSION_COUNT_FB = 63,
+ XR_FACE_EXPRESSION_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFaceExpressionFB;
+
+typedef enum XrFaceExpressionSetFB {
+ XR_FACE_EXPRESSION_SET_DEFAULT_FB = 0,
+ XR_FACE_EXPRESSION_SET_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFaceExpressionSetFB;
+
+typedef enum XrFaceConfidenceFB {
+ XR_FACE_CONFIDENCE_LOWER_FACE_FB = 0,
+ XR_FACE_CONFIDENCE_UPPER_FACE_FB = 1,
+ XR_FACE_CONFIDENCE_COUNT_FB = 2,
+ XR_FACE_CONFIDENCE_MAX_ENUM_FB = 0x7FFFFFFF
+} XrFaceConfidenceFB;
+// XrSystemFaceTrackingPropertiesFB extends XrSystemProperties
+typedef struct XrSystemFaceTrackingPropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsFaceTracking;
+} XrSystemFaceTrackingPropertiesFB;
+
+typedef struct XrFaceTrackerCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFaceExpressionSetFB faceExpressionSet;
+} XrFaceTrackerCreateInfoFB;
+
+typedef struct XrFaceExpressionInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrTime time;
+} XrFaceExpressionInfoFB;
+
+typedef struct XrFaceExpressionStatusFB {
+ XrBool32 isValid;
+ XrBool32 isEyeFollowingBlendshapesValid;
+} XrFaceExpressionStatusFB;
+
+typedef struct XrFaceExpressionWeightsFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t weightCount;
+ float* weights;
+ uint32_t confidenceCount;
+ float* confidences;
+ XrFaceExpressionStatusFB status;
+ XrTime time;
+} XrFaceExpressionWeightsFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateFaceTrackerFB)(XrSession session, const XrFaceTrackerCreateInfoFB* createInfo, XrFaceTrackerFB* faceTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyFaceTrackerFB)(XrFaceTrackerFB faceTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrGetFaceExpressionWeightsFB)(XrFaceTrackerFB faceTracker, const XrFaceExpressionInfoFB* expressionInfo, XrFaceExpressionWeightsFB* expressionWeights);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateFaceTrackerFB(
+ XrSession session,
+ const XrFaceTrackerCreateInfoFB* createInfo,
+ XrFaceTrackerFB* faceTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFaceTrackerFB(
+ XrFaceTrackerFB faceTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceExpressionWeightsFB(
+ XrFaceTrackerFB faceTracker,
+ const XrFaceExpressionInfoFB* expressionInfo,
+ XrFaceExpressionWeightsFB* expressionWeights);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+#define XR_FB_eye_tracking_social 1
+XR_DEFINE_HANDLE(XrEyeTrackerFB)
+#define XR_FB_eye_tracking_social_SPEC_VERSION 1
+#define XR_FB_EYE_TRACKING_SOCIAL_EXTENSION_NAME "XR_FB_eye_tracking_social"
+
+typedef enum XrEyePositionFB {
+ XR_EYE_POSITION_LEFT_FB = 0,
+ XR_EYE_POSITION_RIGHT_FB = 1,
+ XR_EYE_POSITION_COUNT_FB = 2,
+ XR_EYE_POSITION_MAX_ENUM_FB = 0x7FFFFFFF
+} XrEyePositionFB;
+typedef struct XrEyeGazeFB {
+ XrBool32 isValid;
+ XrPosef gazePose;
+ float gazeConfidence;
+} XrEyeGazeFB;
+
+typedef struct XrEyeTrackerCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrEyeTrackerCreateInfoFB;
+
+typedef struct XrEyeGazesInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+} XrEyeGazesInfoFB;
+
+// XrSystemEyeTrackingPropertiesFB extends XrSystemProperties
+typedef struct XrSystemEyeTrackingPropertiesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsEyeTracking;
+} XrSystemEyeTrackingPropertiesFB;
+
+typedef struct XrEyeGazesFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrEyeGazeFB gaze[XR_EYE_POSITION_COUNT_FB];
+ XrTime time;
+} XrEyeGazesFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateEyeTrackerFB)(XrSession session, const XrEyeTrackerCreateInfoFB* createInfo, XrEyeTrackerFB* eyeTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyEyeTrackerFB)(XrEyeTrackerFB eyeTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrGetEyeGazesFB)(XrEyeTrackerFB eyeTracker, const XrEyeGazesInfoFB* gazeInfo, XrEyeGazesFB* eyeGazes);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateEyeTrackerFB(
+ XrSession session,
+ const XrEyeTrackerCreateInfoFB* createInfo,
+ XrEyeTrackerFB* eyeTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEyeTrackerFB(
+ XrEyeTrackerFB eyeTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetEyeGazesFB(
+ XrEyeTrackerFB eyeTracker,
+ const XrEyeGazesInfoFB* gazeInfo,
+ XrEyeGazesFB* eyeGazes);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_FB_passthrough_keyboard_hands 1
#define XR_FB_passthrough_keyboard_hands_SPEC_VERSION 2
#define XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME "XR_FB_passthrough_keyboard_hands"
@@ -4517,6 +5154,349 @@ typedef struct XrCompositionLayerSettingsFB {
+#define XR_FB_touch_controller_proximity 1
+#define XR_FB_touch_controller_proximity_SPEC_VERSION 1
+#define XR_FB_TOUCH_CONTROLLER_PROXIMITY_EXTENSION_NAME "XR_FB_touch_controller_proximity"
+
+
+#define XR_FB_haptic_pcm 1
+
+#define XR_MAX_HAPTIC_PCM_BUFFER_SIZE_FB 4000
+
+#define XR_FB_haptic_pcm_SPEC_VERSION 1
+#define XR_FB_HAPTIC_PCM_EXTENSION_NAME "XR_FB_haptic_pcm"
+typedef struct XrHapticPcmVibrationFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t bufferSize;
+ const float* buffer;
+ float sampleRate;
+ XrBool32 append;
+ uint32_t* samplesConsumed;
+} XrHapticPcmVibrationFB;
+
+typedef struct XrDevicePcmSampleRateStateFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ float sampleRate;
+} XrDevicePcmSampleRateStateFB;
+
+typedef XrDevicePcmSampleRateStateFB XrDevicePcmSampleRateGetInfoFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetDeviceSampleRateFB)(XrSession session, const XrHapticActionInfo* hapticActionInfo, XrDevicePcmSampleRateGetInfoFB* deviceSampleRate);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetDeviceSampleRateFB(
+ XrSession session,
+ const XrHapticActionInfo* hapticActionInfo,
+ XrDevicePcmSampleRateGetInfoFB* deviceSampleRate);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+#define XR_FB_composition_layer_depth_test 1
+#define XR_FB_composition_layer_depth_test_SPEC_VERSION 1
+#define XR_FB_COMPOSITION_LAYER_DEPTH_TEST_EXTENSION_NAME "XR_FB_composition_layer_depth_test"
+
+typedef enum XrCompareOpFB {
+ XR_COMPARE_OP_NEVER_FB = 0,
+ XR_COMPARE_OP_LESS_FB = 1,
+ XR_COMPARE_OP_EQUAL_FB = 2,
+ XR_COMPARE_OP_LESS_OR_EQUAL_FB = 3,
+ XR_COMPARE_OP_GREATER_FB = 4,
+ XR_COMPARE_OP_NOT_EQUAL_FB = 5,
+ XR_COMPARE_OP_GREATER_OR_EQUAL_FB = 6,
+ XR_COMPARE_OP_ALWAYS_FB = 7,
+ XR_COMPARE_OP_MAX_ENUM_FB = 0x7FFFFFFF
+} XrCompareOpFB;
+// XrCompositionLayerDepthTestFB extends XrCompositionLayerBaseHeader
+typedef struct XrCompositionLayerDepthTestFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 depthMask;
+ XrCompareOpFB compareOp;
+} XrCompositionLayerDepthTestFB;
+
+
+
+#define XR_META_local_dimming 1
+#define XR_META_local_dimming_SPEC_VERSION 1
+#define XR_META_LOCAL_DIMMING_EXTENSION_NAME "XR_META_local_dimming"
+
+typedef enum XrLocalDimmingModeMETA {
+ XR_LOCAL_DIMMING_MODE_OFF_META = 0,
+ XR_LOCAL_DIMMING_MODE_ON_META = 1,
+ XR_LOCAL_DIMMING_MODE_MAX_ENUM_META = 0x7FFFFFFF
+} XrLocalDimmingModeMETA;
+// XrLocalDimmingFrameEndInfoMETA extends XrFrameEndInfo
+typedef struct XrLocalDimmingFrameEndInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrLocalDimmingModeMETA localDimmingMode;
+} XrLocalDimmingFrameEndInfoMETA;
+
+
+
+#define XR_META_virtual_keyboard 1
+XR_DEFINE_HANDLE(XrVirtualKeyboardMETA)
+#define XR_MAX_VIRTUAL_KEYBOARD_COMMIT_TEXT_SIZE_META 3992
+#define XR_META_virtual_keyboard_SPEC_VERSION 1
+#define XR_META_VIRTUAL_KEYBOARD_EXTENSION_NAME "XR_META_virtual_keyboard"
+
+typedef enum XrVirtualKeyboardLocationTypeMETA {
+ XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_CUSTOM_META = 0,
+ XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_FAR_META = 1,
+ XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_DIRECT_META = 2,
+ XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_MAX_ENUM_META = 0x7FFFFFFF
+} XrVirtualKeyboardLocationTypeMETA;
+
+typedef enum XrVirtualKeyboardInputSourceMETA {
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_RAY_LEFT_META = 1,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_RAY_RIGHT_META = 2,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_RAY_LEFT_META = 3,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_RAY_RIGHT_META = 4,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_DIRECT_LEFT_META = 5,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_DIRECT_RIGHT_META = 6,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_DIRECT_INDEX_TIP_LEFT_META = 7,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_DIRECT_INDEX_TIP_RIGHT_META = 8,
+ XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_MAX_ENUM_META = 0x7FFFFFFF
+} XrVirtualKeyboardInputSourceMETA;
+typedef XrFlags64 XrVirtualKeyboardInputStateFlagsMETA;
+
+// Flag bits for XrVirtualKeyboardInputStateFlagsMETA
+static const XrVirtualKeyboardInputStateFlagsMETA XR_VIRTUAL_KEYBOARD_INPUT_STATE_PRESSED_BIT_META = 0x00000001;
+
+// XrSystemVirtualKeyboardPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemVirtualKeyboardPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsVirtualKeyboard;
+} XrSystemVirtualKeyboardPropertiesMETA;
+
+typedef struct XrVirtualKeyboardCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrVirtualKeyboardCreateInfoMETA;
+
+typedef struct XrVirtualKeyboardSpaceCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardLocationTypeMETA locationType;
+ XrSpace space;
+ XrPosef poseInSpace;
+} XrVirtualKeyboardSpaceCreateInfoMETA;
+
+typedef struct XrVirtualKeyboardLocationInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardLocationTypeMETA locationType;
+ XrSpace space;
+ XrPosef poseInSpace;
+ float scale;
+} XrVirtualKeyboardLocationInfoMETA;
+
+typedef struct XrVirtualKeyboardModelVisibilitySetInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 visible;
+} XrVirtualKeyboardModelVisibilitySetInfoMETA;
+
+typedef struct XrVirtualKeyboardAnimationStateMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ int32_t animationIndex;
+ float fraction;
+} XrVirtualKeyboardAnimationStateMETA;
+
+typedef struct XrVirtualKeyboardModelAnimationStatesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t stateCapacityInput;
+ uint32_t stateCountOutput;
+ XrVirtualKeyboardAnimationStateMETA* states;
+} XrVirtualKeyboardModelAnimationStatesMETA;
+
+typedef struct XrVirtualKeyboardTextureDataMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t textureWidth;
+ uint32_t textureHeight;
+ uint32_t bufferCapacityInput;
+ uint32_t bufferCountOutput;
+ uint8_t* buffer;
+} XrVirtualKeyboardTextureDataMETA;
+
+typedef struct XrVirtualKeyboardInputInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardInputSourceMETA inputSource;
+ XrSpace inputSpace;
+ XrPosef inputPoseInSpace;
+ XrVirtualKeyboardInputStateFlagsMETA inputState;
+} XrVirtualKeyboardInputInfoMETA;
+
+typedef struct XrVirtualKeyboardTextContextChangeInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ const char* textContext;
+} XrVirtualKeyboardTextContextChangeInfoMETA;
+
+typedef struct XrEventDataVirtualKeyboardCommitTextMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardMETA keyboard;
+ char text[XR_MAX_VIRTUAL_KEYBOARD_COMMIT_TEXT_SIZE_META];
+} XrEventDataVirtualKeyboardCommitTextMETA;
+
+typedef struct XrEventDataVirtualKeyboardBackspaceMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardMETA keyboard;
+} XrEventDataVirtualKeyboardBackspaceMETA;
+
+typedef struct XrEventDataVirtualKeyboardEnterMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardMETA keyboard;
+} XrEventDataVirtualKeyboardEnterMETA;
+
+typedef struct XrEventDataVirtualKeyboardShownMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardMETA keyboard;
+} XrEventDataVirtualKeyboardShownMETA;
+
+typedef struct XrEventDataVirtualKeyboardHiddenMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrVirtualKeyboardMETA keyboard;
+} XrEventDataVirtualKeyboardHiddenMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateVirtualKeyboardMETA)(XrSession session, const XrVirtualKeyboardCreateInfoMETA* createInfo, XrVirtualKeyboardMETA* keyboard);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyVirtualKeyboardMETA)(XrVirtualKeyboardMETA keyboard);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateVirtualKeyboardSpaceMETA)(XrSession session, XrVirtualKeyboardMETA keyboard, const XrVirtualKeyboardSpaceCreateInfoMETA* createInfo, XrSpace* keyboardSpace);
+typedef XrResult (XRAPI_PTR *PFN_xrSuggestVirtualKeyboardLocationMETA)(XrVirtualKeyboardMETA keyboard, const XrVirtualKeyboardLocationInfoMETA* locationInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVirtualKeyboardScaleMETA)(XrVirtualKeyboardMETA keyboard, float* scale);
+typedef XrResult (XRAPI_PTR *PFN_xrSetVirtualKeyboardModelVisibilityMETA)(XrVirtualKeyboardMETA keyboard, const XrVirtualKeyboardModelVisibilitySetInfoMETA* modelVisibility);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVirtualKeyboardModelAnimationStatesMETA)(XrVirtualKeyboardMETA keyboard, XrVirtualKeyboardModelAnimationStatesMETA* animationStates);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVirtualKeyboardDirtyTexturesMETA)(XrVirtualKeyboardMETA keyboard, uint32_t textureIdCapacityInput, uint32_t* textureIdCountOutput, uint64_t* textureIds);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVirtualKeyboardTextureDataMETA)(XrVirtualKeyboardMETA keyboard, uint64_t textureId, XrVirtualKeyboardTextureDataMETA* textureData);
+typedef XrResult (XRAPI_PTR *PFN_xrSendVirtualKeyboardInputMETA)(XrVirtualKeyboardMETA keyboard, const XrVirtualKeyboardInputInfoMETA* info, XrPosef* interactorRootPose);
+typedef XrResult (XRAPI_PTR *PFN_xrChangeVirtualKeyboardTextContextMETA)(XrVirtualKeyboardMETA keyboard, const XrVirtualKeyboardTextContextChangeInfoMETA* changeInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateVirtualKeyboardMETA(
+ XrSession session,
+ const XrVirtualKeyboardCreateInfoMETA* createInfo,
+ XrVirtualKeyboardMETA* keyboard);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyVirtualKeyboardMETA(
+ XrVirtualKeyboardMETA keyboard);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateVirtualKeyboardSpaceMETA(
+ XrSession session,
+ XrVirtualKeyboardMETA keyboard,
+ const XrVirtualKeyboardSpaceCreateInfoMETA* createInfo,
+ XrSpace* keyboardSpace);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSuggestVirtualKeyboardLocationMETA(
+ XrVirtualKeyboardMETA keyboard,
+ const XrVirtualKeyboardLocationInfoMETA* locationInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVirtualKeyboardScaleMETA(
+ XrVirtualKeyboardMETA keyboard,
+ float* scale);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetVirtualKeyboardModelVisibilityMETA(
+ XrVirtualKeyboardMETA keyboard,
+ const XrVirtualKeyboardModelVisibilitySetInfoMETA* modelVisibility);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVirtualKeyboardModelAnimationStatesMETA(
+ XrVirtualKeyboardMETA keyboard,
+ XrVirtualKeyboardModelAnimationStatesMETA* animationStates);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVirtualKeyboardDirtyTexturesMETA(
+ XrVirtualKeyboardMETA keyboard,
+ uint32_t textureIdCapacityInput,
+ uint32_t* textureIdCountOutput,
+ uint64_t* textureIds);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetVirtualKeyboardTextureDataMETA(
+ XrVirtualKeyboardMETA keyboard,
+ uint64_t textureId,
+ XrVirtualKeyboardTextureDataMETA* textureData);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSendVirtualKeyboardInputMETA(
+ XrVirtualKeyboardMETA keyboard,
+ const XrVirtualKeyboardInputInfoMETA* info,
+ XrPosef* interactorRootPose);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrChangeVirtualKeyboardTextContextMETA(
+ XrVirtualKeyboardMETA keyboard,
+ const XrVirtualKeyboardTextContextChangeInfoMETA* changeInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+#define XR_OCULUS_external_camera 1
+#define XR_MAX_EXTERNAL_CAMERA_NAME_SIZE_OCULUS 32
+#define XR_OCULUS_external_camera_SPEC_VERSION 1
+#define XR_OCULUS_EXTERNAL_CAMERA_EXTENSION_NAME "XR_OCULUS_external_camera"
+
+typedef enum XrExternalCameraAttachedToDeviceOCULUS {
+ XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_NONE_OCULUS = 0,
+ XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_HMD_OCULUS = 1,
+ XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_LTOUCH_OCULUS = 2,
+ XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_RTOUCH_OCULUS = 3,
+ XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_MAX_ENUM_OCULUS = 0x7FFFFFFF
+} XrExternalCameraAttachedToDeviceOCULUS;
+typedef XrFlags64 XrExternalCameraStatusFlagsOCULUS;
+
+// Flag bits for XrExternalCameraStatusFlagsOCULUS
+static const XrExternalCameraStatusFlagsOCULUS XR_EXTERNAL_CAMERA_STATUS_CONNECTED_BIT_OCULUS = 0x00000001;
+static const XrExternalCameraStatusFlagsOCULUS XR_EXTERNAL_CAMERA_STATUS_CALIBRATING_BIT_OCULUS = 0x00000002;
+static const XrExternalCameraStatusFlagsOCULUS XR_EXTERNAL_CAMERA_STATUS_CALIBRATION_FAILED_BIT_OCULUS = 0x00000004;
+static const XrExternalCameraStatusFlagsOCULUS XR_EXTERNAL_CAMERA_STATUS_CALIBRATED_BIT_OCULUS = 0x00000008;
+static const XrExternalCameraStatusFlagsOCULUS XR_EXTERNAL_CAMERA_STATUS_CAPTURING_BIT_OCULUS = 0x00000010;
+
+typedef struct XrExternalCameraIntrinsicsOCULUS {
+ XrTime lastChangeTime;
+ XrFovf fov;
+ float virtualNearPlaneDistance;
+ float virtualFarPlaneDistance;
+ XrExtent2Di imageSensorPixelResolution;
+} XrExternalCameraIntrinsicsOCULUS;
+
+typedef struct XrExternalCameraExtrinsicsOCULUS {
+ XrTime lastChangeTime;
+ XrExternalCameraStatusFlagsOCULUS cameraStatusFlags;
+ XrExternalCameraAttachedToDeviceOCULUS attachedToDevice;
+ XrPosef relativePose;
+} XrExternalCameraExtrinsicsOCULUS;
+
+typedef struct XrExternalCameraOCULUS {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ char name[XR_MAX_EXTERNAL_CAMERA_NAME_SIZE_OCULUS];
+ XrExternalCameraIntrinsicsOCULUS intrinsics;
+ XrExternalCameraExtrinsicsOCULUS extrinsics;
+} XrExternalCameraOCULUS;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateExternalCamerasOCULUS)(XrSession session, uint32_t cameraCapacityInput, uint32_t* cameraCountOutput, XrExternalCameraOCULUS* cameras);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateExternalCamerasOCULUS(
+ XrSession session,
+ uint32_t cameraCapacityInput,
+ uint32_t* cameraCountOutput,
+ XrExternalCameraOCULUS* cameras);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_META_performance_metrics 1
#define XR_META_performance_metrics_SPEC_VERSION 2
#define XR_META_PERFORMANCE_METRICS_EXTENSION_NAME "XR_META_performance_metrics"
@@ -4580,6 +5560,67 @@ XRAPI_ATTR XrResult XRAPI_CALL xrQueryPerformanceMetricsCounterMETA(
#endif /* !XR_NO_PROTOTYPES */
+#define XR_FB_spatial_entity_storage_batch 1
+#define XR_FB_spatial_entity_storage_batch_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_STORAGE_BATCH_EXTENSION_NAME "XR_FB_spatial_entity_storage_batch"
+typedef struct XrSpaceListSaveInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t spaceCount;
+ XrSpace* spaces;
+ XrSpaceStorageLocationFB location;
+} XrSpaceListSaveInfoFB;
+
+typedef struct XrEventDataSpaceListSaveCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSpaceListSaveCompleteFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSaveSpaceListFB)(XrSession session, const XrSpaceListSaveInfoFB* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpaceListFB(
+ XrSession session,
+ const XrSpaceListSaveInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+#define XR_FB_spatial_entity_user 1
+typedef uint64_t XrSpaceUserIdFB;
+#define XR_FB_spatial_entity_user_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_USER_EXTENSION_NAME "XR_FB_spatial_entity_user"
+typedef struct XrSpaceUserCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceUserIdFB userId;
+} XrSpaceUserCreateInfoFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpaceUserFB)(XrSession session, const XrSpaceUserCreateInfoFB* info, XrSpaceUserFB* user);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceUserIdFB)(XrSpaceUserFB user, XrSpaceUserIdFB* userId);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpaceUserFB)(XrSpaceUserFB user);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpaceUserFB(
+ XrSession session,
+ const XrSpaceUserCreateInfoFB* info,
+ XrSpaceUserFB* user);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceUserIdFB(
+ XrSpaceUserFB user,
+ XrSpaceUserIdFB* userId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpaceUserFB(
+ XrSpaceUserFB user);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_META_headset_id 1
#define XR_META_headset_id_SPEC_VERSION 1
#define XR_META_HEADSET_ID_EXTENSION_NAME "XR_META_headset_id"
@@ -4592,11 +5633,119 @@ typedef struct XrSystemHeadsetIdPropertiesMETA {
+#define XR_META_passthrough_color_lut 1
+XR_DEFINE_HANDLE(XrPassthroughColorLutMETA)
+#define XR_META_passthrough_color_lut_SPEC_VERSION 1
+#define XR_META_PASSTHROUGH_COLOR_LUT_EXTENSION_NAME "XR_META_passthrough_color_lut"
+
+typedef enum XrPassthroughColorLutChannelsMETA {
+ XR_PASSTHROUGH_COLOR_LUT_CHANNELS_RGB_META = 1,
+ XR_PASSTHROUGH_COLOR_LUT_CHANNELS_RGBA_META = 2,
+ XR_PASSTHROUGH_COLOR_LUT_CHANNELS_MAX_ENUM_META = 0x7FFFFFFF
+} XrPassthroughColorLutChannelsMETA;
+typedef struct XrPassthroughColorLutDataMETA {
+ uint32_t bufferSize;
+ const uint8_t* buffer;
+} XrPassthroughColorLutDataMETA;
+
+typedef struct XrPassthroughColorLutCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughColorLutChannelsMETA channels;
+ uint32_t resolution;
+ XrPassthroughColorLutDataMETA data;
+} XrPassthroughColorLutCreateInfoMETA;
+
+typedef struct XrPassthroughColorLutUpdateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughColorLutDataMETA data;
+} XrPassthroughColorLutUpdateInfoMETA;
+
+// XrPassthroughColorMapLutMETA extends XrPassthroughStyleFB
+typedef struct XrPassthroughColorMapLutMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughColorLutMETA colorLut;
+ float weight;
+} XrPassthroughColorMapLutMETA;
+
+// XrPassthroughColorMapInterpolatedLutMETA extends XrPassthroughStyleFB
+typedef struct XrPassthroughColorMapInterpolatedLutMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPassthroughColorLutMETA sourceColorLut;
+ XrPassthroughColorLutMETA targetColorLut;
+ float weight;
+} XrPassthroughColorMapInterpolatedLutMETA;
+
+// XrSystemPassthroughColorLutPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemPassthroughColorLutPropertiesMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t maxColorLutResolution;
+} XrSystemPassthroughColorLutPropertiesMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreatePassthroughColorLutMETA)(XrPassthroughFB passthrough, const XrPassthroughColorLutCreateInfoMETA* createInfo, XrPassthroughColorLutMETA* colorLut);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyPassthroughColorLutMETA)(XrPassthroughColorLutMETA colorLut);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdatePassthroughColorLutMETA)(XrPassthroughColorLutMETA colorLut, const XrPassthroughColorLutUpdateInfoMETA* updateInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreatePassthroughColorLutMETA(
+ XrPassthroughFB passthrough,
+ const XrPassthroughColorLutCreateInfoMETA* createInfo,
+ XrPassthroughColorLutMETA* colorLut);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughColorLutMETA(
+ XrPassthroughColorLutMETA colorLut);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdatePassthroughColorLutMETA(
+ XrPassthroughColorLutMETA colorLut,
+ const XrPassthroughColorLutUpdateInfoMETA* updateInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_EXT_uuid 1
#define XR_EXT_uuid_SPEC_VERSION 1
#define XR_EXT_UUID_EXTENSION_NAME "XR_EXT_uuid"
+#define XR_EXT_hand_interaction 1
+#define XR_EXT_hand_interaction_SPEC_VERSION 1
+#define XR_EXT_HAND_INTERACTION_EXTENSION_NAME "XR_EXT_hand_interaction"
+
+
+#define XR_QCOM_tracking_optimization_settings 1
+#define XR_QCOM_tracking_optimization_settings_SPEC_VERSION 1
+#define XR_QCOM_TRACKING_OPTIMIZATION_SETTINGS_EXTENSION_NAME "XR_QCOM_tracking_optimization_settings"
+
+typedef enum XrTrackingOptimizationSettingsDomainQCOM {
+ XR_TRACKING_OPTIMIZATION_SETTINGS_DOMAIN_ALL_QCOM = 1,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_DOMAIN_MAX_ENUM_QCOM = 0x7FFFFFFF
+} XrTrackingOptimizationSettingsDomainQCOM;
+
+typedef enum XrTrackingOptimizationSettingsHintQCOM {
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_NONE_QCOM = 0,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_LONG_RANGE_PRIORIZATION_QCOM = 1,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_CLOSE_RANGE_PRIORIZATION_QCOM = 2,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_LOW_POWER_PRIORIZATION_QCOM = 3,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_HIGH_POWER_PRIORIZATION_QCOM = 4,
+ XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_MAX_ENUM_QCOM = 0x7FFFFFFF
+} XrTrackingOptimizationSettingsHintQCOM;
+typedef XrResult (XRAPI_PTR *PFN_xrSetTrackingOptimizationSettingsHintQCOM)(XrSession session, XrTrackingOptimizationSettingsDomainQCOM domain, XrTrackingOptimizationSettingsHintQCOM hint);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetTrackingOptimizationSettingsHintQCOM(
+ XrSession session,
+ XrTrackingOptimizationSettingsDomainQCOM domain,
+ XrTrackingOptimizationSettingsHintQCOM hint);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_HTC_passthrough 1
XR_DEFINE_HANDLE(XrPassthroughHTC)
#define XR_HTC_passthrough_SPEC_VERSION 1
@@ -4741,6 +5890,244 @@ typedef struct XrActiveActionSetPrioritiesEXT {
} XrActiveActionSetPrioritiesEXT;
+
+#define XR_MNDX_force_feedback_curl 1
+#define XR_MNDX_force_feedback_curl_SPEC_VERSION 1
+#define XR_MNDX_FORCE_FEEDBACK_CURL_EXTENSION_NAME "XR_MNDX_force_feedback_curl"
+
+typedef enum XrForceFeedbackCurlLocationMNDX {
+ XR_FORCE_FEEDBACK_CURL_LOCATION_THUMB_CURL_MNDX = 0,
+ XR_FORCE_FEEDBACK_CURL_LOCATION_INDEX_CURL_MNDX = 1,
+ XR_FORCE_FEEDBACK_CURL_LOCATION_MIDDLE_CURL_MNDX = 2,
+ XR_FORCE_FEEDBACK_CURL_LOCATION_RING_CURL_MNDX = 3,
+ XR_FORCE_FEEDBACK_CURL_LOCATION_LITTLE_CURL_MNDX = 4,
+ XR_FORCE_FEEDBACK_CURL_LOCATION_MAX_ENUM_MNDX = 0x7FFFFFFF
+} XrForceFeedbackCurlLocationMNDX;
+// XrSystemForceFeedbackCurlPropertiesMNDX extends XrSystemProperties
+typedef struct XrSystemForceFeedbackCurlPropertiesMNDX {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsForceFeedbackCurl;
+} XrSystemForceFeedbackCurlPropertiesMNDX;
+
+typedef struct XrForceFeedbackCurlApplyLocationMNDX {
+ XrForceFeedbackCurlLocationMNDX location;
+ float value;
+} XrForceFeedbackCurlApplyLocationMNDX;
+
+typedef struct XrForceFeedbackCurlApplyLocationsMNDX {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t locationCount;
+ XrForceFeedbackCurlApplyLocationMNDX* locations;
+} XrForceFeedbackCurlApplyLocationsMNDX;
+
+typedef XrResult (XRAPI_PTR *PFN_xrApplyForceFeedbackCurlMNDX)(XrHandTrackerEXT handTracker, const XrForceFeedbackCurlApplyLocationsMNDX* locations);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrApplyForceFeedbackCurlMNDX(
+ XrHandTrackerEXT handTracker,
+ const XrForceFeedbackCurlApplyLocationsMNDX* locations);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+#define XR_BD_controller_interaction 1
+#define XR_BD_controller_interaction_SPEC_VERSION 1
+#define XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_BD_controller_interaction"
+
+
+#define XR_EXT_local_floor 1
+#define XR_EXT_local_floor_SPEC_VERSION 1
+#define XR_EXT_LOCAL_FLOOR_EXTENSION_NAME "XR_EXT_local_floor"
+
+
+#define XR_EXT_hand_tracking_data_source 1
+#define XR_EXT_hand_tracking_data_source_SPEC_VERSION 1
+#define XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME "XR_EXT_hand_tracking_data_source"
+
+typedef enum XrHandTrackingDataSourceEXT {
+ XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT = 1,
+ XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT = 2,
+ XR_HAND_TRACKING_DATA_SOURCE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrHandTrackingDataSourceEXT;
+// XrHandTrackingDataSourceInfoEXT extends XrHandTrackerCreateInfoEXT
+typedef struct XrHandTrackingDataSourceInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t requestedDataSourceCount;
+ XrHandTrackingDataSourceEXT* requestedDataSources;
+} XrHandTrackingDataSourceInfoEXT;
+
+// XrHandTrackingDataSourceStateEXT extends XrHandJointLocationsEXT
+typedef struct XrHandTrackingDataSourceStateEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 isActive;
+ XrHandTrackingDataSourceEXT dataSource;
+} XrHandTrackingDataSourceStateEXT;
+
+
+
+#define XR_EXT_plane_detection 1
+XR_DEFINE_HANDLE(XrPlaneDetectorEXT)
+#define XR_EXT_plane_detection_SPEC_VERSION 1
+#define XR_EXT_PLANE_DETECTION_EXTENSION_NAME "XR_EXT_plane_detection"
+
+typedef enum XrPlaneDetectorOrientationEXT {
+ XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_UPWARD_EXT = 0,
+ XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_DOWNWARD_EXT = 1,
+ XR_PLANE_DETECTOR_ORIENTATION_VERTICAL_EXT = 2,
+ XR_PLANE_DETECTOR_ORIENTATION_ARBITRARY_EXT = 3,
+ XR_PLANE_DETECTOR_ORIENTATION_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrPlaneDetectorOrientationEXT;
+
+typedef enum XrPlaneDetectorSemanticTypeEXT {
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_UNDEFINED_EXT = 0,
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_CEILING_EXT = 1,
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_FLOOR_EXT = 2,
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_WALL_EXT = 3,
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_PLATFORM_EXT = 4,
+ XR_PLANE_DETECTOR_SEMANTIC_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrPlaneDetectorSemanticTypeEXT;
+
+typedef enum XrPlaneDetectionStateEXT {
+ XR_PLANE_DETECTION_STATE_NONE_EXT = 0,
+ XR_PLANE_DETECTION_STATE_PENDING_EXT = 1,
+ XR_PLANE_DETECTION_STATE_DONE_EXT = 2,
+ XR_PLANE_DETECTION_STATE_ERROR_EXT = 3,
+ XR_PLANE_DETECTION_STATE_FATAL_EXT = 4,
+ XR_PLANE_DETECTION_STATE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrPlaneDetectionStateEXT;
+typedef XrFlags64 XrPlaneDetectionCapabilityFlagsEXT;
+
+// Flag bits for XrPlaneDetectionCapabilityFlagsEXT
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_PLANE_DETECTION_BIT_EXT = 0x00000001;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_PLANE_HOLES_BIT_EXT = 0x00000002;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_CEILING_BIT_EXT = 0x00000004;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_FLOOR_BIT_EXT = 0x00000008;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_WALL_BIT_EXT = 0x00000010;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_PLATFORM_BIT_EXT = 0x00000020;
+static const XrPlaneDetectionCapabilityFlagsEXT XR_PLANE_DETECTION_CAPABILITY_ORIENTATION_BIT_EXT = 0x00000040;
+
+typedef XrFlags64 XrPlaneDetectorFlagsEXT;
+
+// Flag bits for XrPlaneDetectorFlagsEXT
+static const XrPlaneDetectorFlagsEXT XR_PLANE_DETECTOR_ENABLE_CONTOUR_BIT_EXT = 0x00000001;
+
+// XrSystemPlaneDetectionPropertiesEXT extends XrSystemProperties
+typedef struct XrSystemPlaneDetectionPropertiesEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrPlaneDetectionCapabilityFlagsEXT supportedFeatures;
+} XrSystemPlaneDetectionPropertiesEXT;
+
+typedef struct XrPlaneDetectorCreateInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPlaneDetectorFlagsEXT flags;
+} XrPlaneDetectorCreateInfoEXT;
+
+typedef struct XrExtent3DfEXT {
+ float width;
+ float height;
+ float depth;
+} XrExtent3DfEXT;
+
+typedef struct XrPlaneDetectorBeginInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ uint32_t orientationCount;
+ const XrPlaneDetectorOrientationEXT* orientations;
+ uint32_t semanticTypeCount;
+ const XrPlaneDetectorSemanticTypeEXT* semanticTypes;
+ uint32_t maxPlanes;
+ float minArea;
+ XrPosef boundingBoxPose;
+ XrExtent3DfEXT boundingBoxExtent;
+} XrPlaneDetectorBeginInfoEXT;
+
+typedef struct XrPlaneDetectorGetInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+} XrPlaneDetectorGetInfoEXT;
+
+typedef struct XrPlaneDetectorLocationEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint64_t planeId;
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+ XrExtent2Df extents;
+ XrPlaneDetectorOrientationEXT orientation;
+ XrPlaneDetectorSemanticTypeEXT semanticType;
+ uint32_t polygonBufferCount;
+} XrPlaneDetectorLocationEXT;
+
+typedef struct XrPlaneDetectorLocationsEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t planeLocationCapacityInput;
+ uint32_t planeLocationCountOutput;
+ XrPlaneDetectorLocationEXT* planeLocations;
+} XrPlaneDetectorLocationsEXT;
+
+typedef struct XrPlaneDetectorPolygonBufferEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t vertexCapacityInput;
+ uint32_t vertexCountOutput;
+ XrVector2f* vertices;
+} XrPlaneDetectorPolygonBufferEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreatePlaneDetectorEXT)(XrSession session, const XrPlaneDetectorCreateInfoEXT* createInfo, XrPlaneDetectorEXT* planeDetector);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyPlaneDetectorEXT)(XrPlaneDetectorEXT planeDetector);
+typedef XrResult (XRAPI_PTR *PFN_xrBeginPlaneDetectionEXT)(XrPlaneDetectorEXT planeDetector, const XrPlaneDetectorBeginInfoEXT* beginInfo);
+typedef XrResult (XRAPI_PTR *PFN_xrGetPlaneDetectionStateEXT)(XrPlaneDetectorEXT planeDetector, XrPlaneDetectionStateEXT* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetPlaneDetectionsEXT)(XrPlaneDetectorEXT planeDetector, const XrPlaneDetectorGetInfoEXT* info, XrPlaneDetectorLocationsEXT* locations);
+typedef XrResult (XRAPI_PTR *PFN_xrGetPlanePolygonBufferEXT)(XrPlaneDetectorEXT planeDetector, uint64_t planeId, uint32_t polygonBufferIndex, XrPlaneDetectorPolygonBufferEXT* polygonBuffer);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreatePlaneDetectorEXT(
+ XrSession session,
+ const XrPlaneDetectorCreateInfoEXT* createInfo,
+ XrPlaneDetectorEXT* planeDetector);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPlaneDetectorEXT(
+ XrPlaneDetectorEXT planeDetector);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrBeginPlaneDetectionEXT(
+ XrPlaneDetectorEXT planeDetector,
+ const XrPlaneDetectorBeginInfoEXT* beginInfo);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPlaneDetectionStateEXT(
+ XrPlaneDetectorEXT planeDetector,
+ XrPlaneDetectionStateEXT* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPlaneDetectionsEXT(
+ XrPlaneDetectorEXT planeDetector,
+ const XrPlaneDetectorGetInfoEXT* info,
+ XrPlaneDetectorLocationsEXT* locations);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPlanePolygonBufferEXT(
+ XrPlaneDetectorEXT planeDetector,
+ uint64_t planeId,
+ uint32_t polygonBufferIndex,
+ XrPlaneDetectorPolygonBufferEXT* polygonBuffer);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+#define XR_OPPO_controller_interaction 1
+#define XR_OPPO_controller_interaction_SPEC_VERSION 1
+#define XR_OPPO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_OPPO_controller_interaction"
+
#ifdef __cplusplus
}
#endif
diff --git a/thirdparty/openxr/include/openxr/openxr_platform.h b/thirdparty/openxr/include/openxr/openxr_platform.h
index b3aabb23c5..b0a5328f3e 100644
--- a/thirdparty/openxr/include/openxr/openxr_platform.h
+++ b/thirdparty/openxr/include/openxr/openxr_platform.h
@@ -2,7 +2,7 @@
#define OPENXR_PLATFORM_H_ 1
/*
-** Copyright 2017-2022 The Khronos Group Inc.
+** Copyright 2017-2023 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -454,9 +454,9 @@ typedef XrSwapchainImageVulkanKHR XrSwapchainImageVulkan2KHR;
typedef XrGraphicsRequirementsVulkanKHR XrGraphicsRequirementsVulkan2KHR;
-typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanInstanceKHR)(XrInstance instance, const XrVulkanInstanceCreateInfoKHR* createInfo, VkInstance* vulkanInstance, VkResult* vulkanResult);
-typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanDeviceKHR)(XrInstance instance, const XrVulkanDeviceCreateInfoKHR* createInfo, VkDevice* vulkanDevice, VkResult* vulkanResult);
-typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDevice2KHR)(XrInstance instance, const XrVulkanGraphicsDeviceGetInfoKHR* getInfo, VkPhysicalDevice* vulkanPhysicalDevice);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanInstanceKHR)(XrInstance instance, const XrVulkanInstanceCreateInfoKHR* createInfo, VkInstance* vulkanInstance, VkResult* vulkanResult);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanDeviceKHR)(XrInstance instance, const XrVulkanDeviceCreateInfoKHR* createInfo, VkDevice* vulkanDevice, VkResult* vulkanResult);
+typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDevice2KHR)(XrInstance instance, const XrVulkanGraphicsDeviceGetInfoKHR* getInfo, VkPhysicalDevice* vulkanPhysicalDevice);
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsRequirements2KHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
#ifndef XR_NO_PROTOTYPES
@@ -565,6 +565,30 @@ typedef struct XrAndroidSurfaceSwapchainCreateInfoFB {
#endif /* XR_USE_PLATFORM_ANDROID */
+#ifdef XR_USE_PLATFORM_ML
+
+#define XR_ML_compat 1
+#define XR_ML_compat_SPEC_VERSION 1
+#define XR_ML_COMPAT_EXTENSION_NAME "XR_ML_compat"
+typedef struct XrCoordinateSpaceCreateInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ MLCoordinateFrameUID cfuid;
+ XrPosef poseInCoordinateSpace;
+} XrCoordinateSpaceCreateInfoML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpaceFromCoordinateFrameUIDML)(XrSession session, const XrCoordinateSpaceCreateInfoML *createInfo, XrSpace* space);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpaceFromCoordinateFrameUIDML(
+ XrSession session,
+ const XrCoordinateSpaceCreateInfoML * createInfo,
+ XrSpace* space);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_PLATFORM_ML */
+
#ifdef XR_USE_PLATFORM_WIN32
#define XR_OCULUS_audio_device_guid 1
diff --git a/thirdparty/openxr/include/openxr/openxr_platform_defines.h b/thirdparty/openxr/include/openxr/openxr_platform_defines.h
index 31fa05a0c8..820b7b3e1e 100644
--- a/thirdparty/openxr/include/openxr/openxr_platform_defines.h
+++ b/thirdparty/openxr/include/openxr/openxr_platform_defines.h
@@ -1,5 +1,5 @@
/*
-** Copyright (c) 2017-2022, The Khronos Group Inc.
+** Copyright (c) 2017-2023, The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -65,7 +65,7 @@ typedef unsigned __int64 uint64_t;
#endif // !defined( XR_NO_STDINT_H )
// XR_PTR_SIZE (in bytes)
-#if (defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__))
+#if (defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__))
#define XR_PTR_SIZE 8
#else
#define XR_PTR_SIZE 4
@@ -103,6 +103,10 @@ typedef unsigned __int64 uint64_t;
#endif
#endif
+#if !defined(XR_CPP_NULLPTR_SUPPORTED)
+#define XR_CPP_NULLPTR_SUPPORTED 0
+#endif // !defined(XR_CPP_NULLPTR_SUPPORTED)
+
#ifdef __cplusplus
}
#endif
diff --git a/thirdparty/openxr/include/openxr/openxr_reflection.h b/thirdparty/openxr/include/openxr/openxr_reflection.h
index 1a873c1770..c53d412365 100644
--- a/thirdparty/openxr/include/openxr/openxr_reflection.h
+++ b/thirdparty/openxr/include/openxr/openxr_reflection.h
@@ -2,7 +2,7 @@
#define OPENXR_REFLECTION_H_ 1
/*
-** Copyright (c) 2017-2022, The Khronos Group Inc.
+** Copyright (c) 2017-2023, The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -116,6 +116,15 @@ XR_ENUM_STR(XrResult);
_(XR_ERROR_MARKER_ID_INVALID_VARJO, -1000124001) \
_(XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT, -1000142001) \
_(XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT, -1000142002) \
+ _(XR_ERROR_SPACE_MAPPING_INSUFFICIENT_FB, -1000169000) \
+ _(XR_ERROR_SPACE_LOCALIZATION_FAILED_FB, -1000169001) \
+ _(XR_ERROR_SPACE_NETWORK_TIMEOUT_FB, -1000169002) \
+ _(XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB, -1000169003) \
+ _(XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB, -1000169004) \
+ _(XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META, -1000266000) \
+ _(XR_ERROR_HINT_ALREADY_SET_QCOM, -1000306000) \
+ _(XR_ERROR_SPACE_NOT_LOCATABLE_EXT, -1000429000) \
+ _(XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT, -1000429001) \
_(XR_RESULT_MAX_ENUM, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrStructureType(_) \
@@ -242,6 +251,11 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT, 1000066001) \
_(XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB, 1000070000) \
_(XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB, 1000072000) \
+ _(XR_TYPE_BODY_TRACKER_CREATE_INFO_FB, 1000076001) \
+ _(XR_TYPE_BODY_JOINTS_LOCATE_INFO_FB, 1000076002) \
+ _(XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_FB, 1000076004) \
+ _(XR_TYPE_BODY_JOINT_LOCATIONS_FB, 1000076005) \
+ _(XR_TYPE_BODY_SKELETON_FB, 1000076006) \
_(XR_TYPE_INTERACTION_PROFILE_DPAD_BINDING_EXT, 1000078000) \
_(XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE, 1000079000) \
_(XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT, 1000080000) \
@@ -322,6 +336,9 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_VARJO, 1000124000) \
_(XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO, 1000124001) \
_(XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO, 1000124002) \
+ _(XR_TYPE_FRAME_END_INFO_ML, 1000135000) \
+ _(XR_TYPE_GLOBAL_DIMMER_FRAME_END_INFO_ML, 1000136000) \
+ _(XR_TYPE_COORDINATE_SPACE_CREATE_INFO_ML, 1000137000) \
_(XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT, 1000142000) \
_(XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT, 1000142001) \
_(XR_TYPE_SPACE_QUERY_INFO_FB, 1000156001) \
@@ -339,19 +356,64 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB, 1000161000) \
_(XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB, 1000162000) \
_(XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB, 1000163000) \
+ _(XR_TYPE_SPACE_SHARE_INFO_FB, 1000169001) \
+ _(XR_TYPE_EVENT_DATA_SPACE_SHARE_COMPLETE_FB, 1000169002) \
_(XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB, 1000171000) \
_(XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB, 1000171001) \
+ _(XR_TYPE_HAPTIC_AMPLITUDE_ENVELOPE_VIBRATION_FB, 1000173001) \
_(XR_TYPE_SEMANTIC_LABELS_FB, 1000175000) \
_(XR_TYPE_ROOM_LAYOUT_FB, 1000175001) \
_(XR_TYPE_BOUNDARY_2D_FB, 1000175002) \
+ _(XR_TYPE_SEMANTIC_LABELS_SUPPORT_INFO_FB, 1000175010) \
_(XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE, 1000196000) \
+ _(XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB, 1000198001) \
+ _(XR_TYPE_SCENE_CAPTURE_REQUEST_INFO_FB, 1000198050) \
_(XR_TYPE_SPACE_CONTAINER_FB, 1000199000) \
+ _(XR_TYPE_FOVEATION_EYE_TRACKED_PROFILE_CREATE_INFO_META, 1000200000) \
+ _(XR_TYPE_FOVEATION_EYE_TRACKED_STATE_META, 1000200001) \
+ _(XR_TYPE_SYSTEM_FOVEATION_EYE_TRACKED_PROPERTIES_META, 1000200002) \
+ _(XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_FB, 1000201004) \
+ _(XR_TYPE_FACE_TRACKER_CREATE_INFO_FB, 1000201005) \
+ _(XR_TYPE_FACE_EXPRESSION_INFO_FB, 1000201002) \
+ _(XR_TYPE_FACE_EXPRESSION_WEIGHTS_FB, 1000201006) \
+ _(XR_TYPE_EYE_TRACKER_CREATE_INFO_FB, 1000202001) \
+ _(XR_TYPE_EYE_GAZES_INFO_FB, 1000202002) \
+ _(XR_TYPE_EYE_GAZES_FB, 1000202003) \
+ _(XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_FB, 1000202004) \
_(XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB, 1000203002) \
_(XR_TYPE_COMPOSITION_LAYER_SETTINGS_FB, 1000204000) \
+ _(XR_TYPE_HAPTIC_PCM_VIBRATION_FB, 1000209001) \
+ _(XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB, 1000209002) \
+ _(XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_FB, 1000212000) \
+ _(XR_TYPE_LOCAL_DIMMING_FRAME_END_INFO_META, 1000216000) \
+ _(XR_TYPE_SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META, 1000219001) \
+ _(XR_TYPE_VIRTUAL_KEYBOARD_CREATE_INFO_META, 1000219002) \
+ _(XR_TYPE_VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META, 1000219003) \
+ _(XR_TYPE_VIRTUAL_KEYBOARD_LOCATION_INFO_META, 1000219004) \
+ _(XR_TYPE_VIRTUAL_KEYBOARD_MODEL_VISIBILITY_SET_INFO_META, 1000219005) \
+ _(XR_TYPE_VIRTUAL_KEYBOARD_ANIMATION_STATE_META, 1000219006) \
+ _(XR_TYPE_VIRTUAL_KEYBOARD_MODEL_ANIMATION_STATES_META, 1000219007) \
+ _(XR_TYPE_VIRTUAL_KEYBOARD_TEXTURE_DATA_META, 1000219009) \
+ _(XR_TYPE_VIRTUAL_KEYBOARD_INPUT_INFO_META, 1000219010) \
+ _(XR_TYPE_VIRTUAL_KEYBOARD_TEXT_CONTEXT_CHANGE_INFO_META, 1000219011) \
+ _(XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_COMMIT_TEXT_META, 1000219014) \
+ _(XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_BACKSPACE_META, 1000219015) \
+ _(XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_ENTER_META, 1000219016) \
+ _(XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_SHOWN_META, 1000219017) \
+ _(XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_HIDDEN_META, 1000219018) \
+ _(XR_TYPE_EXTERNAL_CAMERA_OCULUS, 1000226000) \
_(XR_TYPE_VULKAN_SWAPCHAIN_CREATE_INFO_META, 1000227000) \
_(XR_TYPE_PERFORMANCE_METRICS_STATE_META, 1000232001) \
_(XR_TYPE_PERFORMANCE_METRICS_COUNTER_META, 1000232002) \
+ _(XR_TYPE_SPACE_LIST_SAVE_INFO_FB, 1000238000) \
+ _(XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB, 1000238001) \
+ _(XR_TYPE_SPACE_USER_CREATE_INFO_FB, 1000241001) \
_(XR_TYPE_SYSTEM_HEADSET_ID_PROPERTIES_META, 1000245000) \
+ _(XR_TYPE_SYSTEM_PASSTHROUGH_COLOR_LUT_PROPERTIES_META, 1000266000) \
+ _(XR_TYPE_PASSTHROUGH_COLOR_LUT_CREATE_INFO_META, 1000266001) \
+ _(XR_TYPE_PASSTHROUGH_COLOR_LUT_UPDATE_INFO_META, 1000266002) \
+ _(XR_TYPE_PASSTHROUGH_COLOR_MAP_LUT_META, 1000266100) \
+ _(XR_TYPE_PASSTHROUGH_COLOR_MAP_INTERPOLATED_LUT_META, 1000266101) \
_(XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC, 1000317001) \
_(XR_TYPE_PASSTHROUGH_COLOR_HTC, 1000317002) \
_(XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC, 1000317003) \
@@ -360,6 +422,17 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_FOVEATION_DYNAMIC_MODE_INFO_HTC, 1000318001) \
_(XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC, 1000318002) \
_(XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT, 1000373000) \
+ _(XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX, 1000375000) \
+ _(XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX, 1000375001) \
+ _(XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, 1000428000) \
+ _(XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT, 1000428001) \
+ _(XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT, 1000429001) \
+ _(XR_TYPE_PLANE_DETECTOR_BEGIN_INFO_EXT, 1000429002) \
+ _(XR_TYPE_PLANE_DETECTOR_GET_INFO_EXT, 1000429003) \
+ _(XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT, 1000429004) \
+ _(XR_TYPE_PLANE_DETECTOR_LOCATION_EXT, 1000429005) \
+ _(XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT, 1000429006) \
+ _(XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT, 1000429007) \
_(XR_STRUCTURE_TYPE_MAX_ENUM, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrFormFactor(_) \
@@ -386,6 +459,7 @@ XR_ENUM_STR(XrResult);
_(XR_REFERENCE_SPACE_TYPE_STAGE, 3) \
_(XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT, 1000038000) \
_(XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO, 1000121000) \
+ _(XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT, 1000426000) \
_(XR_REFERENCE_SPACE_TYPE_MAX_ENUM, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrActionType(_) \
@@ -426,6 +500,7 @@ XR_ENUM_STR(XrResult);
_(XR_OBJECT_TYPE_SPATIAL_ANCHOR_MSFT, 1000039000) \
_(XR_OBJECT_TYPE_SPATIAL_GRAPH_NODE_BINDING_MSFT, 1000049000) \
_(XR_OBJECT_TYPE_HAND_TRACKER_EXT, 1000051000) \
+ _(XR_OBJECT_TYPE_BODY_TRACKER_FB, 1000076000) \
_(XR_OBJECT_TYPE_SCENE_OBSERVER_MSFT, 1000097000) \
_(XR_OBJECT_TYPE_SCENE_MSFT, 1000097001) \
_(XR_OBJECT_TYPE_FACIAL_TRACKER_HTC, 1000104000) \
@@ -435,7 +510,13 @@ XR_ENUM_STR(XrResult);
_(XR_OBJECT_TYPE_PASSTHROUGH_LAYER_FB, 1000118002) \
_(XR_OBJECT_TYPE_GEOMETRY_INSTANCE_FB, 1000118004) \
_(XR_OBJECT_TYPE_SPATIAL_ANCHOR_STORE_CONNECTION_MSFT, 1000142000) \
+ _(XR_OBJECT_TYPE_FACE_TRACKER_FB, 1000201000) \
+ _(XR_OBJECT_TYPE_EYE_TRACKER_FB, 1000202000) \
+ _(XR_OBJECT_TYPE_VIRTUAL_KEYBOARD_META, 1000219000) \
+ _(XR_OBJECT_TYPE_SPACE_USER_FB, 1000241000) \
+ _(XR_OBJECT_TYPE_PASSTHROUGH_COLOR_LUT_META, 1000266000) \
_(XR_OBJECT_TYPE_PASSTHROUGH_HTC, 1000317000) \
+ _(XR_OBJECT_TYPE_PLANE_DETECTOR_EXT, 1000429000) \
_(XR_OBJECT_TYPE_MAX_ENUM, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrAndroidThreadTypeKHR(_) \
@@ -540,6 +621,85 @@ XR_ENUM_STR(XrResult);
_(XR_REPROJECTION_MODE_ORIENTATION_ONLY_MSFT, 4) \
_(XR_REPROJECTION_MODE_MAX_ENUM_MSFT, 0x7FFFFFFF)
+#define XR_LIST_ENUM_XrBodyJointFB(_) \
+ _(XR_BODY_JOINT_ROOT_FB, 0) \
+ _(XR_BODY_JOINT_HIPS_FB, 1) \
+ _(XR_BODY_JOINT_SPINE_LOWER_FB, 2) \
+ _(XR_BODY_JOINT_SPINE_MIDDLE_FB, 3) \
+ _(XR_BODY_JOINT_SPINE_UPPER_FB, 4) \
+ _(XR_BODY_JOINT_CHEST_FB, 5) \
+ _(XR_BODY_JOINT_NECK_FB, 6) \
+ _(XR_BODY_JOINT_HEAD_FB, 7) \
+ _(XR_BODY_JOINT_LEFT_SHOULDER_FB, 8) \
+ _(XR_BODY_JOINT_LEFT_SCAPULA_FB, 9) \
+ _(XR_BODY_JOINT_LEFT_ARM_UPPER_FB, 10) \
+ _(XR_BODY_JOINT_LEFT_ARM_LOWER_FB, 11) \
+ _(XR_BODY_JOINT_LEFT_HAND_WRIST_TWIST_FB, 12) \
+ _(XR_BODY_JOINT_RIGHT_SHOULDER_FB, 13) \
+ _(XR_BODY_JOINT_RIGHT_SCAPULA_FB, 14) \
+ _(XR_BODY_JOINT_RIGHT_ARM_UPPER_FB, 15) \
+ _(XR_BODY_JOINT_RIGHT_ARM_LOWER_FB, 16) \
+ _(XR_BODY_JOINT_RIGHT_HAND_WRIST_TWIST_FB, 17) \
+ _(XR_BODY_JOINT_LEFT_HAND_PALM_FB, 18) \
+ _(XR_BODY_JOINT_LEFT_HAND_WRIST_FB, 19) \
+ _(XR_BODY_JOINT_LEFT_HAND_THUMB_METACARPAL_FB, 20) \
+ _(XR_BODY_JOINT_LEFT_HAND_THUMB_PROXIMAL_FB, 21) \
+ _(XR_BODY_JOINT_LEFT_HAND_THUMB_DISTAL_FB, 22) \
+ _(XR_BODY_JOINT_LEFT_HAND_THUMB_TIP_FB, 23) \
+ _(XR_BODY_JOINT_LEFT_HAND_INDEX_METACARPAL_FB, 24) \
+ _(XR_BODY_JOINT_LEFT_HAND_INDEX_PROXIMAL_FB, 25) \
+ _(XR_BODY_JOINT_LEFT_HAND_INDEX_INTERMEDIATE_FB, 26) \
+ _(XR_BODY_JOINT_LEFT_HAND_INDEX_DISTAL_FB, 27) \
+ _(XR_BODY_JOINT_LEFT_HAND_INDEX_TIP_FB, 28) \
+ _(XR_BODY_JOINT_LEFT_HAND_MIDDLE_METACARPAL_FB, 29) \
+ _(XR_BODY_JOINT_LEFT_HAND_MIDDLE_PROXIMAL_FB, 30) \
+ _(XR_BODY_JOINT_LEFT_HAND_MIDDLE_INTERMEDIATE_FB, 31) \
+ _(XR_BODY_JOINT_LEFT_HAND_MIDDLE_DISTAL_FB, 32) \
+ _(XR_BODY_JOINT_LEFT_HAND_MIDDLE_TIP_FB, 33) \
+ _(XR_BODY_JOINT_LEFT_HAND_RING_METACARPAL_FB, 34) \
+ _(XR_BODY_JOINT_LEFT_HAND_RING_PROXIMAL_FB, 35) \
+ _(XR_BODY_JOINT_LEFT_HAND_RING_INTERMEDIATE_FB, 36) \
+ _(XR_BODY_JOINT_LEFT_HAND_RING_DISTAL_FB, 37) \
+ _(XR_BODY_JOINT_LEFT_HAND_RING_TIP_FB, 38) \
+ _(XR_BODY_JOINT_LEFT_HAND_LITTLE_METACARPAL_FB, 39) \
+ _(XR_BODY_JOINT_LEFT_HAND_LITTLE_PROXIMAL_FB, 40) \
+ _(XR_BODY_JOINT_LEFT_HAND_LITTLE_INTERMEDIATE_FB, 41) \
+ _(XR_BODY_JOINT_LEFT_HAND_LITTLE_DISTAL_FB, 42) \
+ _(XR_BODY_JOINT_LEFT_HAND_LITTLE_TIP_FB, 43) \
+ _(XR_BODY_JOINT_RIGHT_HAND_PALM_FB, 44) \
+ _(XR_BODY_JOINT_RIGHT_HAND_WRIST_FB, 45) \
+ _(XR_BODY_JOINT_RIGHT_HAND_THUMB_METACARPAL_FB, 46) \
+ _(XR_BODY_JOINT_RIGHT_HAND_THUMB_PROXIMAL_FB, 47) \
+ _(XR_BODY_JOINT_RIGHT_HAND_THUMB_DISTAL_FB, 48) \
+ _(XR_BODY_JOINT_RIGHT_HAND_THUMB_TIP_FB, 49) \
+ _(XR_BODY_JOINT_RIGHT_HAND_INDEX_METACARPAL_FB, 50) \
+ _(XR_BODY_JOINT_RIGHT_HAND_INDEX_PROXIMAL_FB, 51) \
+ _(XR_BODY_JOINT_RIGHT_HAND_INDEX_INTERMEDIATE_FB, 52) \
+ _(XR_BODY_JOINT_RIGHT_HAND_INDEX_DISTAL_FB, 53) \
+ _(XR_BODY_JOINT_RIGHT_HAND_INDEX_TIP_FB, 54) \
+ _(XR_BODY_JOINT_RIGHT_HAND_MIDDLE_METACARPAL_FB, 55) \
+ _(XR_BODY_JOINT_RIGHT_HAND_MIDDLE_PROXIMAL_FB, 56) \
+ _(XR_BODY_JOINT_RIGHT_HAND_MIDDLE_INTERMEDIATE_FB, 57) \
+ _(XR_BODY_JOINT_RIGHT_HAND_MIDDLE_DISTAL_FB, 58) \
+ _(XR_BODY_JOINT_RIGHT_HAND_MIDDLE_TIP_FB, 59) \
+ _(XR_BODY_JOINT_RIGHT_HAND_RING_METACARPAL_FB, 60) \
+ _(XR_BODY_JOINT_RIGHT_HAND_RING_PROXIMAL_FB, 61) \
+ _(XR_BODY_JOINT_RIGHT_HAND_RING_INTERMEDIATE_FB, 62) \
+ _(XR_BODY_JOINT_RIGHT_HAND_RING_DISTAL_FB, 63) \
+ _(XR_BODY_JOINT_RIGHT_HAND_RING_TIP_FB, 64) \
+ _(XR_BODY_JOINT_RIGHT_HAND_LITTLE_METACARPAL_FB, 65) \
+ _(XR_BODY_JOINT_RIGHT_HAND_LITTLE_PROXIMAL_FB, 66) \
+ _(XR_BODY_JOINT_RIGHT_HAND_LITTLE_INTERMEDIATE_FB, 67) \
+ _(XR_BODY_JOINT_RIGHT_HAND_LITTLE_DISTAL_FB, 68) \
+ _(XR_BODY_JOINT_RIGHT_HAND_LITTLE_TIP_FB, 69) \
+ _(XR_BODY_JOINT_COUNT_FB, 70) \
+ _(XR_BODY_JOINT_NONE_FB, -1) \
+ _(XR_BODY_JOINT_MAX_ENUM_FB, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrBodyJointSetFB(_) \
+ _(XR_BODY_JOINT_SET_DEFAULT_FB, 0) \
+ _(XR_BODY_JOINT_SET_MAX_ENUM_FB, 0x7FFFFFFF)
+
#define XR_LIST_ENUM_XrHandJointsMotionRangeEXT(_) \
_(XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT, 1) \
_(XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT, 2) \
@@ -674,6 +834,7 @@ XR_ENUM_STR(XrResult);
#define XR_LIST_ENUM_XrSpaceComponentTypeFB(_) \
_(XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB, 0) \
_(XR_SPACE_COMPONENT_TYPE_STORABLE_FB, 1) \
+ _(XR_SPACE_COMPONENT_TYPE_SHARABLE_FB, 2) \
_(XR_SPACE_COMPONENT_TYPE_BOUNDED_2D_FB, 3) \
_(XR_SPACE_COMPONENT_TYPE_BOUNDED_3D_FB, 4) \
_(XR_SPACE_COMPONENT_TYPE_SEMANTIC_LABELS_FB, 5) \
@@ -743,6 +904,7 @@ XR_ENUM_STR(XrResult);
#define XR_LIST_ENUM_XrSpaceStorageLocationFB(_) \
_(XR_SPACE_STORAGE_LOCATION_INVALID_FB, 0) \
_(XR_SPACE_STORAGE_LOCATION_LOCAL_FB, 1) \
+ _(XR_SPACE_STORAGE_LOCATION_CLOUD_FB, 2) \
_(XR_SPACE_STORAGE_LOCATION_MAX_ENUM_FB, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrSpacePersistenceModeFB(_) \
@@ -750,6 +912,129 @@ XR_ENUM_STR(XrResult);
_(XR_SPACE_PERSISTENCE_MODE_INDEFINITE_FB, 1) \
_(XR_SPACE_PERSISTENCE_MODE_MAX_ENUM_FB, 0x7FFFFFFF)
+#define XR_LIST_ENUM_XrFaceExpressionFB(_) \
+ _(XR_FACE_EXPRESSION_BROW_LOWERER_L_FB, 0) \
+ _(XR_FACE_EXPRESSION_BROW_LOWERER_R_FB, 1) \
+ _(XR_FACE_EXPRESSION_CHEEK_PUFF_L_FB, 2) \
+ _(XR_FACE_EXPRESSION_CHEEK_PUFF_R_FB, 3) \
+ _(XR_FACE_EXPRESSION_CHEEK_RAISER_L_FB, 4) \
+ _(XR_FACE_EXPRESSION_CHEEK_RAISER_R_FB, 5) \
+ _(XR_FACE_EXPRESSION_CHEEK_SUCK_L_FB, 6) \
+ _(XR_FACE_EXPRESSION_CHEEK_SUCK_R_FB, 7) \
+ _(XR_FACE_EXPRESSION_CHIN_RAISER_B_FB, 8) \
+ _(XR_FACE_EXPRESSION_CHIN_RAISER_T_FB, 9) \
+ _(XR_FACE_EXPRESSION_DIMPLER_L_FB, 10) \
+ _(XR_FACE_EXPRESSION_DIMPLER_R_FB, 11) \
+ _(XR_FACE_EXPRESSION_EYES_CLOSED_L_FB, 12) \
+ _(XR_FACE_EXPRESSION_EYES_CLOSED_R_FB, 13) \
+ _(XR_FACE_EXPRESSION_EYES_LOOK_DOWN_L_FB, 14) \
+ _(XR_FACE_EXPRESSION_EYES_LOOK_DOWN_R_FB, 15) \
+ _(XR_FACE_EXPRESSION_EYES_LOOK_LEFT_L_FB, 16) \
+ _(XR_FACE_EXPRESSION_EYES_LOOK_LEFT_R_FB, 17) \
+ _(XR_FACE_EXPRESSION_EYES_LOOK_RIGHT_L_FB, 18) \
+ _(XR_FACE_EXPRESSION_EYES_LOOK_RIGHT_R_FB, 19) \
+ _(XR_FACE_EXPRESSION_EYES_LOOK_UP_L_FB, 20) \
+ _(XR_FACE_EXPRESSION_EYES_LOOK_UP_R_FB, 21) \
+ _(XR_FACE_EXPRESSION_INNER_BROW_RAISER_L_FB, 22) \
+ _(XR_FACE_EXPRESSION_INNER_BROW_RAISER_R_FB, 23) \
+ _(XR_FACE_EXPRESSION_JAW_DROP_FB, 24) \
+ _(XR_FACE_EXPRESSION_JAW_SIDEWAYS_LEFT_FB, 25) \
+ _(XR_FACE_EXPRESSION_JAW_SIDEWAYS_RIGHT_FB, 26) \
+ _(XR_FACE_EXPRESSION_JAW_THRUST_FB, 27) \
+ _(XR_FACE_EXPRESSION_LID_TIGHTENER_L_FB, 28) \
+ _(XR_FACE_EXPRESSION_LID_TIGHTENER_R_FB, 29) \
+ _(XR_FACE_EXPRESSION_LIP_CORNER_DEPRESSOR_L_FB, 30) \
+ _(XR_FACE_EXPRESSION_LIP_CORNER_DEPRESSOR_R_FB, 31) \
+ _(XR_FACE_EXPRESSION_LIP_CORNER_PULLER_L_FB, 32) \
+ _(XR_FACE_EXPRESSION_LIP_CORNER_PULLER_R_FB, 33) \
+ _(XR_FACE_EXPRESSION_LIP_FUNNELER_LB_FB, 34) \
+ _(XR_FACE_EXPRESSION_LIP_FUNNELER_LT_FB, 35) \
+ _(XR_FACE_EXPRESSION_LIP_FUNNELER_RB_FB, 36) \
+ _(XR_FACE_EXPRESSION_LIP_FUNNELER_RT_FB, 37) \
+ _(XR_FACE_EXPRESSION_LIP_PRESSOR_L_FB, 38) \
+ _(XR_FACE_EXPRESSION_LIP_PRESSOR_R_FB, 39) \
+ _(XR_FACE_EXPRESSION_LIP_PUCKER_L_FB, 40) \
+ _(XR_FACE_EXPRESSION_LIP_PUCKER_R_FB, 41) \
+ _(XR_FACE_EXPRESSION_LIP_STRETCHER_L_FB, 42) \
+ _(XR_FACE_EXPRESSION_LIP_STRETCHER_R_FB, 43) \
+ _(XR_FACE_EXPRESSION_LIP_SUCK_LB_FB, 44) \
+ _(XR_FACE_EXPRESSION_LIP_SUCK_LT_FB, 45) \
+ _(XR_FACE_EXPRESSION_LIP_SUCK_RB_FB, 46) \
+ _(XR_FACE_EXPRESSION_LIP_SUCK_RT_FB, 47) \
+ _(XR_FACE_EXPRESSION_LIP_TIGHTENER_L_FB, 48) \
+ _(XR_FACE_EXPRESSION_LIP_TIGHTENER_R_FB, 49) \
+ _(XR_FACE_EXPRESSION_LIPS_TOWARD_FB, 50) \
+ _(XR_FACE_EXPRESSION_LOWER_LIP_DEPRESSOR_L_FB, 51) \
+ _(XR_FACE_EXPRESSION_LOWER_LIP_DEPRESSOR_R_FB, 52) \
+ _(XR_FACE_EXPRESSION_MOUTH_LEFT_FB, 53) \
+ _(XR_FACE_EXPRESSION_MOUTH_RIGHT_FB, 54) \
+ _(XR_FACE_EXPRESSION_NOSE_WRINKLER_L_FB, 55) \
+ _(XR_FACE_EXPRESSION_NOSE_WRINKLER_R_FB, 56) \
+ _(XR_FACE_EXPRESSION_OUTER_BROW_RAISER_L_FB, 57) \
+ _(XR_FACE_EXPRESSION_OUTER_BROW_RAISER_R_FB, 58) \
+ _(XR_FACE_EXPRESSION_UPPER_LID_RAISER_L_FB, 59) \
+ _(XR_FACE_EXPRESSION_UPPER_LID_RAISER_R_FB, 60) \
+ _(XR_FACE_EXPRESSION_UPPER_LIP_RAISER_L_FB, 61) \
+ _(XR_FACE_EXPRESSION_UPPER_LIP_RAISER_R_FB, 62) \
+ _(XR_FACE_EXPRESSION_COUNT_FB, 63) \
+ _(XR_FACE_EXPRESSION_MAX_ENUM_FB, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrFaceExpressionSetFB(_) \
+ _(XR_FACE_EXPRESSION_SET_DEFAULT_FB, 0) \
+ _(XR_FACE_EXPRESSION_SET_MAX_ENUM_FB, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrFaceConfidenceFB(_) \
+ _(XR_FACE_CONFIDENCE_LOWER_FACE_FB, 0) \
+ _(XR_FACE_CONFIDENCE_UPPER_FACE_FB, 1) \
+ _(XR_FACE_CONFIDENCE_COUNT_FB, 2) \
+ _(XR_FACE_CONFIDENCE_MAX_ENUM_FB, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrEyePositionFB(_) \
+ _(XR_EYE_POSITION_LEFT_FB, 0) \
+ _(XR_EYE_POSITION_RIGHT_FB, 1) \
+ _(XR_EYE_POSITION_COUNT_FB, 2) \
+ _(XR_EYE_POSITION_MAX_ENUM_FB, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrCompareOpFB(_) \
+ _(XR_COMPARE_OP_NEVER_FB, 0) \
+ _(XR_COMPARE_OP_LESS_FB, 1) \
+ _(XR_COMPARE_OP_EQUAL_FB, 2) \
+ _(XR_COMPARE_OP_LESS_OR_EQUAL_FB, 3) \
+ _(XR_COMPARE_OP_GREATER_FB, 4) \
+ _(XR_COMPARE_OP_NOT_EQUAL_FB, 5) \
+ _(XR_COMPARE_OP_GREATER_OR_EQUAL_FB, 6) \
+ _(XR_COMPARE_OP_ALWAYS_FB, 7) \
+ _(XR_COMPARE_OPFB_MAX_ENUM_FB, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrLocalDimmingModeMETA(_) \
+ _(XR_LOCAL_DIMMING_MODE_OFF_META, 0) \
+ _(XR_LOCAL_DIMMING_MODE_ON_META, 1) \
+ _(XR_LOCAL_DIMMING_MODE_MAX_ENUM_META, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrVirtualKeyboardLocationTypeMETA(_) \
+ _(XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_CUSTOM_META, 0) \
+ _(XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_FAR_META, 1) \
+ _(XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_DIRECT_META, 2) \
+ _(XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_MAX_ENUM_META, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrVirtualKeyboardInputSourceMETA(_) \
+ _(XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_RAY_LEFT_META, 1) \
+ _(XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_RAY_RIGHT_META, 2) \
+ _(XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_RAY_LEFT_META, 3) \
+ _(XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_RAY_RIGHT_META, 4) \
+ _(XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_DIRECT_LEFT_META, 5) \
+ _(XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_DIRECT_RIGHT_META, 6) \
+ _(XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_DIRECT_INDEX_TIP_LEFT_META, 7) \
+ _(XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_DIRECT_INDEX_TIP_RIGHT_META, 8) \
+ _(XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_MAX_ENUM_META, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrExternalCameraAttachedToDeviceOCULUS(_) \
+ _(XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_NONE_OCULUS, 0) \
+ _(XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_HMD_OCULUS, 1) \
+ _(XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_LTOUCH_OCULUS, 2) \
+ _(XR_EXTERNAL_CAMERA_ATTACHED_TO_DEVICE_RTOUCH_OCULUS, 3) \
+ _(XR_EXTERNAL_CAMERA_ATTACHED_TODEVICE_MAX_ENUM_OCULUS, 0x7FFFFFFF)
+
#define XR_LIST_ENUM_XrPerformanceMetricsCounterUnitMETA(_) \
_(XR_PERFORMANCE_METRICS_COUNTER_UNIT_GENERIC_META, 0) \
_(XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META, 1) \
@@ -758,6 +1043,23 @@ XR_ENUM_STR(XrResult);
_(XR_PERFORMANCE_METRICS_COUNTER_UNIT_HERTZ_META, 4) \
_(XR_PERFORMANCE_METRICS_COUNTER_UNIT_MAX_ENUM_META, 0x7FFFFFFF)
+#define XR_LIST_ENUM_XrPassthroughColorLutChannelsMETA(_) \
+ _(XR_PASSTHROUGH_COLOR_LUT_CHANNELS_RGB_META, 1) \
+ _(XR_PASSTHROUGH_COLOR_LUT_CHANNELS_RGBA_META, 2) \
+ _(XR_PASSTHROUGH_COLOR_LUT_CHANNELS_MAX_ENUM_META, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrTrackingOptimizationSettingsDomainQCOM(_) \
+ _(XR_TRACKING_OPTIMIZATION_SETTINGS_DOMAIN_ALL_QCOM, 1) \
+ _(XR_TRACKING_OPTIMIZATION_SETTINGS_DOMAIN_MAX_ENUM_QCOM, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrTrackingOptimizationSettingsHintQCOM(_) \
+ _(XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_NONE_QCOM, 0) \
+ _(XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_LONG_RANGE_PRIORIZATION_QCOM, 1) \
+ _(XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_CLOSE_RANGE_PRIORIZATION_QCOM, 2) \
+ _(XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_LOW_POWER_PRIORIZATION_QCOM, 3) \
+ _(XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_HIGH_POWER_PRIORIZATION_QCOM, 4) \
+ _(XR_TRACKING_OPTIMIZATION_SETTINGS_HINT_MAX_ENUM_QCOM, 0x7FFFFFFF)
+
#define XR_LIST_ENUM_XrPassthroughFormHTC(_) \
_(XR_PASSTHROUGH_FORM_PLANAR_HTC, 0) \
_(XR_PASSTHROUGH_FORM_PROJECTED_HTC, 1) \
@@ -777,6 +1079,42 @@ XR_ENUM_STR(XrResult);
_(XR_FOVEATION_LEVEL_HIGH_HTC, 3) \
_(XR_FOVEATION_LEVEL_MAX_ENUM_HTC, 0x7FFFFFFF)
+#define XR_LIST_ENUM_XrForceFeedbackCurlLocationMNDX(_) \
+ _(XR_FORCE_FEEDBACK_CURL_LOCATION_THUMB_CURL_MNDX, 0) \
+ _(XR_FORCE_FEEDBACK_CURL_LOCATION_INDEX_CURL_MNDX, 1) \
+ _(XR_FORCE_FEEDBACK_CURL_LOCATION_MIDDLE_CURL_MNDX, 2) \
+ _(XR_FORCE_FEEDBACK_CURL_LOCATION_RING_CURL_MNDX, 3) \
+ _(XR_FORCE_FEEDBACK_CURL_LOCATION_LITTLE_CURL_MNDX, 4) \
+ _(XR_FORCE_FEEDBACK_CURL_LOCATION_MAX_ENUM_MNDX, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrHandTrackingDataSourceEXT(_) \
+ _(XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT, 1) \
+ _(XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT, 2) \
+ _(XR_HAND_TRACKING_DATA_SOURCE_MAX_ENUM_EXT, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrPlaneDetectorOrientationEXT(_) \
+ _(XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_UPWARD_EXT, 0) \
+ _(XR_PLANE_DETECTOR_ORIENTATION_HORIZONTAL_DOWNWARD_EXT, 1) \
+ _(XR_PLANE_DETECTOR_ORIENTATION_VERTICAL_EXT, 2) \
+ _(XR_PLANE_DETECTOR_ORIENTATION_ARBITRARY_EXT, 3) \
+ _(XR_PLANE_DETECTOR_ORIENTATION_MAX_ENUM_EXT, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrPlaneDetectorSemanticTypeEXT(_) \
+ _(XR_PLANE_DETECTOR_SEMANTIC_TYPE_UNDEFINED_EXT, 0) \
+ _(XR_PLANE_DETECTOR_SEMANTIC_TYPE_CEILING_EXT, 1) \
+ _(XR_PLANE_DETECTOR_SEMANTIC_TYPE_FLOOR_EXT, 2) \
+ _(XR_PLANE_DETECTOR_SEMANTIC_TYPE_WALL_EXT, 3) \
+ _(XR_PLANE_DETECTOR_SEMANTIC_TYPE_PLATFORM_EXT, 4) \
+ _(XR_PLANE_DETECTOR_SEMANTIC_TYPE_MAX_ENUM_EXT, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrPlaneDetectionStateEXT(_) \
+ _(XR_PLANE_DETECTION_STATE_NONE_EXT, 0) \
+ _(XR_PLANE_DETECTION_STATE_PENDING_EXT, 1) \
+ _(XR_PLANE_DETECTION_STATE_DONE_EXT, 2) \
+ _(XR_PLANE_DETECTION_STATE_ERROR_EXT, 3) \
+ _(XR_PLANE_DETECTION_STATE_FATAL_EXT, 4) \
+ _(XR_PLANE_DETECTION_STATE_MAX_ENUM_EXT, 0x7FFFFFFF)
+
#define XR_LIST_BITS_XrInstanceCreateFlags(_)
#define XR_LIST_BITS_XrSessionCreateFlags(_)
@@ -903,18 +1241,44 @@ XR_ENUM_STR(XrResult);
_(XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_1_BIT_FB, 0x00000001) \
_(XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_2_BIT_FB, 0x00000002) \
+#define XR_LIST_BITS_XrFrameEndInfoFlagsML(_) \
+ _(XR_FRAME_END_INFO_PROTECTED_BIT_ML, 0x00000001) \
+ _(XR_FRAME_END_INFO_VIGNETTE_BIT_ML, 0x00000002) \
+
+#define XR_LIST_BITS_XrGlobalDimmerFrameEndInfoFlagsML(_) \
+ _(XR_GLOBAL_DIMMER_FRAME_END_INFO_ENABLED_BIT_ML, 0x00000001) \
+
#define XR_LIST_BITS_XrCompositionLayerSpaceWarpInfoFlagsFB(_) \
_(XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0x00000001) \
+#define XR_LIST_BITS_XrSemanticLabelsSupportFlagsFB(_) \
+ _(XR_SEMANTIC_LABELS_SUPPORT_MULTIPLE_SEMANTIC_LABELS_BIT_FB, 0x00000001) \
+ _(XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_DESK_TO_TABLE_MIGRATION_BIT_FB, 0x00000002) \
+
#define XR_LIST_BITS_XrDigitalLensControlFlagsALMALENCE(_) \
_(XR_DIGITAL_LENS_CONTROL_PROCESSING_DISABLE_BIT_ALMALENCE, 0x00000001) \
+#define XR_LIST_BITS_XrFoveationEyeTrackedProfileCreateFlagsMETA(_)
+
+#define XR_LIST_BITS_XrFoveationEyeTrackedStateFlagsMETA(_) \
+ _(XR_FOVEATION_EYE_TRACKED_STATE_VALID_BIT_META, 0x00000001) \
+
#define XR_LIST_BITS_XrCompositionLayerSettingsFlagsFB(_) \
_(XR_COMPOSITION_LAYER_SETTINGS_NORMAL_SUPER_SAMPLING_BIT_FB, 0x00000001) \
_(XR_COMPOSITION_LAYER_SETTINGS_QUALITY_SUPER_SAMPLING_BIT_FB, 0x00000002) \
_(XR_COMPOSITION_LAYER_SETTINGS_NORMAL_SHARPENING_BIT_FB, 0x00000004) \
_(XR_COMPOSITION_LAYER_SETTINGS_QUALITY_SHARPENING_BIT_FB, 0x00000008) \
+#define XR_LIST_BITS_XrVirtualKeyboardInputStateFlagsMETA(_) \
+ _(XR_VIRTUAL_KEYBOARD_INPUT_STATE_PRESSED_BIT_META, 0x00000001) \
+
+#define XR_LIST_BITS_XrExternalCameraStatusFlagsOCULUS(_) \
+ _(XR_EXTERNAL_CAMERA_STATUS_CONNECTED_BIT_OCULUS, 0x00000001) \
+ _(XR_EXTERNAL_CAMERA_STATUS_CALIBRATING_BIT_OCULUS, 0x00000002) \
+ _(XR_EXTERNAL_CAMERA_STATUS_CALIBRATION_FAILED_BIT_OCULUS, 0x00000004) \
+ _(XR_EXTERNAL_CAMERA_STATUS_CALIBRATED_BIT_OCULUS, 0x00000008) \
+ _(XR_EXTERNAL_CAMERA_STATUS_CAPTURING_BIT_OCULUS, 0x00000010) \
+
#define XR_LIST_BITS_XrPerformanceMetricsCounterFlagsMETA(_) \
_(XR_PERFORMANCE_METRICS_COUNTER_ANY_VALUE_VALID_BIT_META, 0x00000001) \
_(XR_PERFORMANCE_METRICS_COUNTER_UINT_VALUE_VALID_BIT_META, 0x00000002) \
@@ -925,6 +1289,18 @@ XR_ENUM_STR(XrResult);
_(XR_FOVEATION_DYNAMIC_CLEAR_FOV_ENABLED_BIT_HTC, 0x00000002) \
_(XR_FOVEATION_DYNAMIC_FOCAL_CENTER_OFFSET_ENABLED_BIT_HTC, 0x00000004) \
+#define XR_LIST_BITS_XrPlaneDetectionCapabilityFlagsEXT(_) \
+ _(XR_PLANE_DETECTION_CAPABILITY_PLANE_DETECTION_BIT_EXT, 0x00000001) \
+ _(XR_PLANE_DETECTION_CAPABILITY_PLANE_HOLES_BIT_EXT, 0x00000002) \
+ _(XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_CEILING_BIT_EXT, 0x00000004) \
+ _(XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_FLOOR_BIT_EXT, 0x00000008) \
+ _(XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_WALL_BIT_EXT, 0x00000010) \
+ _(XR_PLANE_DETECTION_CAPABILITY_SEMANTIC_PLATFORM_BIT_EXT, 0x00000020) \
+ _(XR_PLANE_DETECTION_CAPABILITY_ORIENTATION_BIT_EXT, 0x00000040) \
+
+#define XR_LIST_BITS_XrPlaneDetectorFlagsEXT(_) \
+ _(XR_PLANE_DETECTOR_ENABLE_CONTOUR_BIT_EXT, 0x00000001) \
+
/// Calls your macro with the name of each member of XrApiLayerProperties, in order.
#define XR_LIST_STRUCT_XrApiLayerProperties(_) \
_(type) \
@@ -2095,6 +2471,54 @@ XR_ENUM_STR(XrResult);
_(next) \
_(flags) \
+/// Calls your macro with the name of each member of XrBodyJointLocationFB, in order.
+#define XR_LIST_STRUCT_XrBodyJointLocationFB(_) \
+ _(locationFlags) \
+ _(pose) \
+
+/// Calls your macro with the name of each member of XrSystemBodyTrackingPropertiesFB, in order.
+#define XR_LIST_STRUCT_XrSystemBodyTrackingPropertiesFB(_) \
+ _(type) \
+ _(next) \
+ _(supportsBodyTracking) \
+
+/// Calls your macro with the name of each member of XrBodyTrackerCreateInfoFB, in order.
+#define XR_LIST_STRUCT_XrBodyTrackerCreateInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(bodyJointSet) \
+
+/// Calls your macro with the name of each member of XrBodySkeletonJointFB, in order.
+#define XR_LIST_STRUCT_XrBodySkeletonJointFB(_) \
+ _(joint) \
+ _(parentJoint) \
+ _(pose) \
+
+/// Calls your macro with the name of each member of XrBodySkeletonFB, in order.
+#define XR_LIST_STRUCT_XrBodySkeletonFB(_) \
+ _(type) \
+ _(next) \
+ _(jointCount) \
+ _(joints) \
+
+/// Calls your macro with the name of each member of XrBodyJointsLocateInfoFB, in order.
+#define XR_LIST_STRUCT_XrBodyJointsLocateInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(baseSpace) \
+ _(time) \
+
+/// Calls your macro with the name of each member of XrBodyJointLocationsFB, in order.
+#define XR_LIST_STRUCT_XrBodyJointLocationsFB(_) \
+ _(type) \
+ _(next) \
+ _(isActive) \
+ _(confidence) \
+ _(jointCount) \
+ _(jointLocations) \
+ _(skeletonChangedCount) \
+ _(time) \
+
/// Calls your macro with the name of each member of XrInteractionProfileDpadBindingEXT, in order.
#define XR_LIST_STRUCT_XrInteractionProfileDpadBindingEXT(_) \
_(type) \
@@ -2731,6 +3155,27 @@ XR_ENUM_STR(XrResult);
_(markerId) \
_(poseInMarkerSpace) \
+/// Calls your macro with the name of each member of XrFrameEndInfoML, in order.
+#define XR_LIST_STRUCT_XrFrameEndInfoML(_) \
+ _(type) \
+ _(next) \
+ _(focusDistance) \
+ _(flags) \
+
+/// Calls your macro with the name of each member of XrGlobalDimmerFrameEndInfoML, in order.
+#define XR_LIST_STRUCT_XrGlobalDimmerFrameEndInfoML(_) \
+ _(type) \
+ _(next) \
+ _(dimmerValue) \
+ _(flags) \
+
+/// Calls your macro with the name of each member of XrCoordinateSpaceCreateInfoML, in order.
+#define XR_LIST_STRUCT_XrCoordinateSpaceCreateInfoML(_) \
+ _(type) \
+ _(next) \
+ _(cfuid) \
+ _(poseInCoordinateSpace) \
+
/// Calls your macro with the name of each member of XrSpatialAnchorPersistenceNameMSFT, in order.
#define XR_LIST_STRUCT_XrSpatialAnchorPersistenceNameMSFT(_) \
_(name) \
@@ -2895,6 +3340,22 @@ XR_ENUM_STR(XrResult);
_(maxAnisotropy) \
_(borderColor) \
+/// Calls your macro with the name of each member of XrSpaceShareInfoFB, in order.
+#define XR_LIST_STRUCT_XrSpaceShareInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(spaceCount) \
+ _(spaces) \
+ _(userCount) \
+ _(users) \
+
+/// Calls your macro with the name of each member of XrEventDataSpaceShareCompleteFB, in order.
+#define XR_LIST_STRUCT_XrEventDataSpaceShareCompleteFB(_) \
+ _(type) \
+ _(next) \
+ _(requestId) \
+ _(result) \
+
/// Calls your macro with the name of each member of XrCompositionLayerSpaceWarpInfoFB, in order.
#define XR_LIST_STRUCT_XrCompositionLayerSpaceWarpInfoFB(_) \
_(type) \
@@ -2915,6 +3376,14 @@ XR_ENUM_STR(XrResult);
_(recommendedMotionVectorImageRectWidth) \
_(recommendedMotionVectorImageRectHeight) \
+/// Calls your macro with the name of each member of XrHapticAmplitudeEnvelopeVibrationFB, in order.
+#define XR_LIST_STRUCT_XrHapticAmplitudeEnvelopeVibrationFB(_) \
+ _(type) \
+ _(next) \
+ _(duration) \
+ _(amplitudeCount) \
+ _(amplitudes) \
+
/// Calls your macro with the name of each member of XrExtent3DfFB, in order.
#define XR_LIST_STRUCT_XrExtent3DfFB(_) \
_(width) \
@@ -2958,12 +3427,33 @@ XR_ENUM_STR(XrResult);
_(vertexCountOutput) \
_(vertices) \
+/// Calls your macro with the name of each member of XrSemanticLabelsSupportInfoFB, in order.
+#define XR_LIST_STRUCT_XrSemanticLabelsSupportInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(flags) \
+ _(recognizedLabels) \
+
/// Calls your macro with the name of each member of XrDigitalLensControlALMALENCE, in order.
#define XR_LIST_STRUCT_XrDigitalLensControlALMALENCE(_) \
_(type) \
_(next) \
_(flags) \
+/// Calls your macro with the name of each member of XrEventDataSceneCaptureCompleteFB, in order.
+#define XR_LIST_STRUCT_XrEventDataSceneCaptureCompleteFB(_) \
+ _(type) \
+ _(next) \
+ _(requestId) \
+ _(result) \
+
+/// Calls your macro with the name of each member of XrSceneCaptureRequestInfoFB, in order.
+#define XR_LIST_STRUCT_XrSceneCaptureRequestInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(requestByteCount) \
+ _(request) \
+
/// Calls your macro with the name of each member of XrSpaceContainerFB, in order.
#define XR_LIST_STRUCT_XrSpaceContainerFB(_) \
_(type) \
@@ -2972,6 +3462,90 @@ XR_ENUM_STR(XrResult);
_(uuidCountOutput) \
_(uuids) \
+/// Calls your macro with the name of each member of XrFoveationEyeTrackedProfileCreateInfoMETA, in order.
+#define XR_LIST_STRUCT_XrFoveationEyeTrackedProfileCreateInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(flags) \
+
+/// Calls your macro with the name of each member of XrFoveationEyeTrackedStateMETA, in order.
+#define XR_LIST_STRUCT_XrFoveationEyeTrackedStateMETA(_) \
+ _(type) \
+ _(next) \
+ _(foveationCenter) \
+ _(flags) \
+
+/// Calls your macro with the name of each member of XrSystemFoveationEyeTrackedPropertiesMETA, in order.
+#define XR_LIST_STRUCT_XrSystemFoveationEyeTrackedPropertiesMETA(_) \
+ _(type) \
+ _(next) \
+ _(supportsFoveationEyeTracked) \
+
+/// Calls your macro with the name of each member of XrSystemFaceTrackingPropertiesFB, in order.
+#define XR_LIST_STRUCT_XrSystemFaceTrackingPropertiesFB(_) \
+ _(type) \
+ _(next) \
+ _(supportsFaceTracking) \
+
+/// Calls your macro with the name of each member of XrFaceTrackerCreateInfoFB, in order.
+#define XR_LIST_STRUCT_XrFaceTrackerCreateInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(faceExpressionSet) \
+
+/// Calls your macro with the name of each member of XrFaceExpressionInfoFB, in order.
+#define XR_LIST_STRUCT_XrFaceExpressionInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(time) \
+
+/// Calls your macro with the name of each member of XrFaceExpressionStatusFB, in order.
+#define XR_LIST_STRUCT_XrFaceExpressionStatusFB(_) \
+ _(isValid) \
+ _(isEyeFollowingBlendshapesValid) \
+
+/// Calls your macro with the name of each member of XrFaceExpressionWeightsFB, in order.
+#define XR_LIST_STRUCT_XrFaceExpressionWeightsFB(_) \
+ _(type) \
+ _(next) \
+ _(weightCount) \
+ _(weights) \
+ _(confidenceCount) \
+ _(confidences) \
+ _(status) \
+ _(time) \
+
+/// Calls your macro with the name of each member of XrEyeGazeFB, in order.
+#define XR_LIST_STRUCT_XrEyeGazeFB(_) \
+ _(isValid) \
+ _(gazePose) \
+ _(gazeConfidence) \
+
+/// Calls your macro with the name of each member of XrEyeTrackerCreateInfoFB, in order.
+#define XR_LIST_STRUCT_XrEyeTrackerCreateInfoFB(_) \
+ _(type) \
+ _(next) \
+
+/// Calls your macro with the name of each member of XrEyeGazesInfoFB, in order.
+#define XR_LIST_STRUCT_XrEyeGazesInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(baseSpace) \
+ _(time) \
+
+/// Calls your macro with the name of each member of XrSystemEyeTrackingPropertiesFB, in order.
+#define XR_LIST_STRUCT_XrSystemEyeTrackingPropertiesFB(_) \
+ _(type) \
+ _(next) \
+ _(supportsEyeTracking) \
+
+/// Calls your macro with the name of each member of XrEyeGazesFB, in order.
+#define XR_LIST_STRUCT_XrEyeGazesFB(_) \
+ _(type) \
+ _(next) \
+ _(gaze) \
+ _(time) \
+
/// Calls your macro with the name of each member of XrPassthroughKeyboardHandsIntensityFB, in order.
#define XR_LIST_STRUCT_XrPassthroughKeyboardHandsIntensityFB(_) \
_(type) \
@@ -2985,6 +3559,163 @@ XR_ENUM_STR(XrResult);
_(next) \
_(layerFlags) \
+/// Calls your macro with the name of each member of XrHapticPcmVibrationFB, in order.
+#define XR_LIST_STRUCT_XrHapticPcmVibrationFB(_) \
+ _(type) \
+ _(next) \
+ _(bufferSize) \
+ _(buffer) \
+ _(sampleRate) \
+ _(append) \
+ _(samplesConsumed) \
+
+/// Calls your macro with the name of each member of XrDevicePcmSampleRateStateFB, in order.
+#define XR_LIST_STRUCT_XrDevicePcmSampleRateStateFB(_) \
+ _(type) \
+ _(next) \
+ _(sampleRate) \
+
+/// Calls your macro with the name of each member of XrCompositionLayerDepthTestFB, in order.
+#define XR_LIST_STRUCT_XrCompositionLayerDepthTestFB(_) \
+ _(type) \
+ _(next) \
+ _(depthMask) \
+ _(compareOp) \
+
+/// Calls your macro with the name of each member of XrLocalDimmingFrameEndInfoMETA, in order.
+#define XR_LIST_STRUCT_XrLocalDimmingFrameEndInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(localDimmingMode) \
+
+/// Calls your macro with the name of each member of XrSystemVirtualKeyboardPropertiesMETA, in order.
+#define XR_LIST_STRUCT_XrSystemVirtualKeyboardPropertiesMETA(_) \
+ _(type) \
+ _(next) \
+ _(supportsVirtualKeyboard) \
+
+/// Calls your macro with the name of each member of XrVirtualKeyboardCreateInfoMETA, in order.
+#define XR_LIST_STRUCT_XrVirtualKeyboardCreateInfoMETA(_) \
+ _(type) \
+ _(next) \
+
+/// Calls your macro with the name of each member of XrVirtualKeyboardSpaceCreateInfoMETA, in order.
+#define XR_LIST_STRUCT_XrVirtualKeyboardSpaceCreateInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(locationType) \
+ _(space) \
+ _(poseInSpace) \
+
+/// Calls your macro with the name of each member of XrVirtualKeyboardLocationInfoMETA, in order.
+#define XR_LIST_STRUCT_XrVirtualKeyboardLocationInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(locationType) \
+ _(space) \
+ _(poseInSpace) \
+ _(scale) \
+
+/// Calls your macro with the name of each member of XrVirtualKeyboardModelVisibilitySetInfoMETA, in order.
+#define XR_LIST_STRUCT_XrVirtualKeyboardModelVisibilitySetInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(visible) \
+
+/// Calls your macro with the name of each member of XrVirtualKeyboardAnimationStateMETA, in order.
+#define XR_LIST_STRUCT_XrVirtualKeyboardAnimationStateMETA(_) \
+ _(type) \
+ _(next) \
+ _(animationIndex) \
+ _(fraction) \
+
+/// Calls your macro with the name of each member of XrVirtualKeyboardModelAnimationStatesMETA, in order.
+#define XR_LIST_STRUCT_XrVirtualKeyboardModelAnimationStatesMETA(_) \
+ _(type) \
+ _(next) \
+ _(stateCapacityInput) \
+ _(stateCountOutput) \
+ _(states) \
+
+/// Calls your macro with the name of each member of XrVirtualKeyboardTextureDataMETA, in order.
+#define XR_LIST_STRUCT_XrVirtualKeyboardTextureDataMETA(_) \
+ _(type) \
+ _(next) \
+ _(textureWidth) \
+ _(textureHeight) \
+ _(bufferCapacityInput) \
+ _(bufferCountOutput) \
+ _(buffer) \
+
+/// Calls your macro with the name of each member of XrVirtualKeyboardInputInfoMETA, in order.
+#define XR_LIST_STRUCT_XrVirtualKeyboardInputInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(inputSource) \
+ _(inputSpace) \
+ _(inputPoseInSpace) \
+ _(inputState) \
+
+/// Calls your macro with the name of each member of XrVirtualKeyboardTextContextChangeInfoMETA, in order.
+#define XR_LIST_STRUCT_XrVirtualKeyboardTextContextChangeInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(textContext) \
+
+/// Calls your macro with the name of each member of XrEventDataVirtualKeyboardCommitTextMETA, in order.
+#define XR_LIST_STRUCT_XrEventDataVirtualKeyboardCommitTextMETA(_) \
+ _(type) \
+ _(next) \
+ _(keyboard) \
+ _(text) \
+
+/// Calls your macro with the name of each member of XrEventDataVirtualKeyboardBackspaceMETA, in order.
+#define XR_LIST_STRUCT_XrEventDataVirtualKeyboardBackspaceMETA(_) \
+ _(type) \
+ _(next) \
+ _(keyboard) \
+
+/// Calls your macro with the name of each member of XrEventDataVirtualKeyboardEnterMETA, in order.
+#define XR_LIST_STRUCT_XrEventDataVirtualKeyboardEnterMETA(_) \
+ _(type) \
+ _(next) \
+ _(keyboard) \
+
+/// Calls your macro with the name of each member of XrEventDataVirtualKeyboardShownMETA, in order.
+#define XR_LIST_STRUCT_XrEventDataVirtualKeyboardShownMETA(_) \
+ _(type) \
+ _(next) \
+ _(keyboard) \
+
+/// Calls your macro with the name of each member of XrEventDataVirtualKeyboardHiddenMETA, in order.
+#define XR_LIST_STRUCT_XrEventDataVirtualKeyboardHiddenMETA(_) \
+ _(type) \
+ _(next) \
+ _(keyboard) \
+
+/// Calls your macro with the name of each member of XrExternalCameraIntrinsicsOCULUS, in order.
+#define XR_LIST_STRUCT_XrExternalCameraIntrinsicsOCULUS(_) \
+ _(lastChangeTime) \
+ _(fov) \
+ _(virtualNearPlaneDistance) \
+ _(virtualFarPlaneDistance) \
+ _(imageSensorPixelResolution) \
+
+/// Calls your macro with the name of each member of XrExternalCameraExtrinsicsOCULUS, in order.
+#define XR_LIST_STRUCT_XrExternalCameraExtrinsicsOCULUS(_) \
+ _(lastChangeTime) \
+ _(cameraStatusFlags) \
+ _(attachedToDevice) \
+ _(relativePose) \
+
+/// Calls your macro with the name of each member of XrExternalCameraOCULUS, in order.
+#define XR_LIST_STRUCT_XrExternalCameraOCULUS(_) \
+ _(type) \
+ _(next) \
+ _(name) \
+ _(intrinsics) \
+ _(extrinsics) \
+
/// Calls your macro with the name of each member of XrVulkanSwapchainCreateInfoMETA, in order.
#define XR_LIST_STRUCT_XrVulkanSwapchainCreateInfoMETA(_) \
_(type) \
@@ -3007,12 +3738,73 @@ XR_ENUM_STR(XrResult);
_(uintValue) \
_(floatValue) \
+/// Calls your macro with the name of each member of XrSpaceListSaveInfoFB, in order.
+#define XR_LIST_STRUCT_XrSpaceListSaveInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(spaceCount) \
+ _(spaces) \
+ _(location) \
+
+/// Calls your macro with the name of each member of XrEventDataSpaceListSaveCompleteFB, in order.
+#define XR_LIST_STRUCT_XrEventDataSpaceListSaveCompleteFB(_) \
+ _(type) \
+ _(next) \
+ _(requestId) \
+ _(result) \
+
+/// Calls your macro with the name of each member of XrSpaceUserCreateInfoFB, in order.
+#define XR_LIST_STRUCT_XrSpaceUserCreateInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(userId) \
+
/// Calls your macro with the name of each member of XrSystemHeadsetIdPropertiesMETA, in order.
#define XR_LIST_STRUCT_XrSystemHeadsetIdPropertiesMETA(_) \
_(type) \
_(next) \
_(id) \
+/// Calls your macro with the name of each member of XrPassthroughColorLutDataMETA, in order.
+#define XR_LIST_STRUCT_XrPassthroughColorLutDataMETA(_) \
+ _(bufferSize) \
+ _(buffer) \
+
+/// Calls your macro with the name of each member of XrPassthroughColorLutCreateInfoMETA, in order.
+#define XR_LIST_STRUCT_XrPassthroughColorLutCreateInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(channels) \
+ _(resolution) \
+ _(data) \
+
+/// Calls your macro with the name of each member of XrPassthroughColorLutUpdateInfoMETA, in order.
+#define XR_LIST_STRUCT_XrPassthroughColorLutUpdateInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(data) \
+
+/// Calls your macro with the name of each member of XrPassthroughColorMapLutMETA, in order.
+#define XR_LIST_STRUCT_XrPassthroughColorMapLutMETA(_) \
+ _(type) \
+ _(next) \
+ _(colorLut) \
+ _(weight) \
+
+/// Calls your macro with the name of each member of XrPassthroughColorMapInterpolatedLutMETA, in order.
+#define XR_LIST_STRUCT_XrPassthroughColorMapInterpolatedLutMETA(_) \
+ _(type) \
+ _(next) \
+ _(sourceColorLut) \
+ _(targetColorLut) \
+ _(weight) \
+
+/// Calls your macro with the name of each member of XrSystemPassthroughColorLutPropertiesMETA, in order.
+#define XR_LIST_STRUCT_XrSystemPassthroughColorLutPropertiesMETA(_) \
+ _(type) \
+ _(next) \
+ _(maxColorLutResolution) \
+
/// Calls your macro with the name of each member of XrPassthroughCreateInfoHTC, in order.
#define XR_LIST_STRUCT_XrPassthroughCreateInfoHTC(_) \
_(type) \
@@ -3086,6 +3878,106 @@ XR_ENUM_STR(XrResult);
_(actionSetPriorityCount) \
_(actionSetPriorities) \
+/// Calls your macro with the name of each member of XrSystemForceFeedbackCurlPropertiesMNDX, in order.
+#define XR_LIST_STRUCT_XrSystemForceFeedbackCurlPropertiesMNDX(_) \
+ _(type) \
+ _(next) \
+ _(supportsForceFeedbackCurl) \
+
+/// Calls your macro with the name of each member of XrForceFeedbackCurlApplyLocationMNDX, in order.
+#define XR_LIST_STRUCT_XrForceFeedbackCurlApplyLocationMNDX(_) \
+ _(location) \
+ _(value) \
+
+/// Calls your macro with the name of each member of XrForceFeedbackCurlApplyLocationsMNDX, in order.
+#define XR_LIST_STRUCT_XrForceFeedbackCurlApplyLocationsMNDX(_) \
+ _(type) \
+ _(next) \
+ _(locationCount) \
+ _(locations) \
+
+/// Calls your macro with the name of each member of XrHandTrackingDataSourceInfoEXT, in order.
+#define XR_LIST_STRUCT_XrHandTrackingDataSourceInfoEXT(_) \
+ _(type) \
+ _(next) \
+ _(requestedDataSourceCount) \
+ _(requestedDataSources) \
+
+/// Calls your macro with the name of each member of XrHandTrackingDataSourceStateEXT, in order.
+#define XR_LIST_STRUCT_XrHandTrackingDataSourceStateEXT(_) \
+ _(type) \
+ _(next) \
+ _(isActive) \
+ _(dataSource) \
+
+/// Calls your macro with the name of each member of XrSystemPlaneDetectionPropertiesEXT, in order.
+#define XR_LIST_STRUCT_XrSystemPlaneDetectionPropertiesEXT(_) \
+ _(type) \
+ _(next) \
+ _(supportedFeatures) \
+
+/// Calls your macro with the name of each member of XrPlaneDetectorCreateInfoEXT, in order.
+#define XR_LIST_STRUCT_XrPlaneDetectorCreateInfoEXT(_) \
+ _(type) \
+ _(next) \
+ _(flags) \
+
+/// Calls your macro with the name of each member of XrExtent3DfEXT, in order.
+#define XR_LIST_STRUCT_XrExtent3DfEXT(_) \
+ _(width) \
+ _(height) \
+ _(depth) \
+
+/// Calls your macro with the name of each member of XrPlaneDetectorBeginInfoEXT, in order.
+#define XR_LIST_STRUCT_XrPlaneDetectorBeginInfoEXT(_) \
+ _(type) \
+ _(next) \
+ _(baseSpace) \
+ _(time) \
+ _(orientationCount) \
+ _(orientations) \
+ _(semanticTypeCount) \
+ _(semanticTypes) \
+ _(maxPlanes) \
+ _(minArea) \
+ _(boundingBoxPose) \
+ _(boundingBoxExtent) \
+
+/// Calls your macro with the name of each member of XrPlaneDetectorGetInfoEXT, in order.
+#define XR_LIST_STRUCT_XrPlaneDetectorGetInfoEXT(_) \
+ _(type) \
+ _(next) \
+ _(baseSpace) \
+ _(time) \
+
+/// Calls your macro with the name of each member of XrPlaneDetectorLocationEXT, in order.
+#define XR_LIST_STRUCT_XrPlaneDetectorLocationEXT(_) \
+ _(type) \
+ _(next) \
+ _(planeId) \
+ _(locationFlags) \
+ _(pose) \
+ _(extents) \
+ _(orientation) \
+ _(semanticType) \
+ _(polygonBufferCount) \
+
+/// Calls your macro with the name of each member of XrPlaneDetectorLocationsEXT, in order.
+#define XR_LIST_STRUCT_XrPlaneDetectorLocationsEXT(_) \
+ _(type) \
+ _(next) \
+ _(planeLocationCapacityInput) \
+ _(planeLocationCountOutput) \
+ _(planeLocations) \
+
+/// Calls your macro with the name of each member of XrPlaneDetectorPolygonBufferEXT, in order.
+#define XR_LIST_STRUCT_XrPlaneDetectorPolygonBufferEXT(_) \
+ _(type) \
+ _(next) \
+ _(vertexCapacityInput) \
+ _(vertexCountOutput) \
+ _(vertices) \
+
/// Calls your macro with the structure type name and the XrStructureType constant for
@@ -3104,6 +3996,7 @@ XR_ENUM_STR(XrResult);
XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_VULKAN(_) \
XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_ANDROID(_) \
XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_EGL(_) \
+ XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_ML(_) \
XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_WIN32(_) \
@@ -3210,6 +4103,11 @@ XR_ENUM_STR(XrResult);
_(XrCompositionLayerReprojectionInfoMSFT, XR_TYPE_COMPOSITION_LAYER_REPROJECTION_INFO_MSFT) \
_(XrCompositionLayerReprojectionPlaneOverrideMSFT, XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT) \
_(XrCompositionLayerSecureContentFB, XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB) \
+ _(XrSystemBodyTrackingPropertiesFB, XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_FB) \
+ _(XrBodyTrackerCreateInfoFB, XR_TYPE_BODY_TRACKER_CREATE_INFO_FB) \
+ _(XrBodySkeletonFB, XR_TYPE_BODY_SKELETON_FB) \
+ _(XrBodyJointsLocateInfoFB, XR_TYPE_BODY_JOINTS_LOCATE_INFO_FB) \
+ _(XrBodyJointLocationsFB, XR_TYPE_BODY_JOINT_LOCATIONS_FB) \
_(XrInteractionProfileDpadBindingEXT, XR_TYPE_INTERACTION_PROFILE_DPAD_BINDING_EXT) \
_(XrInteractionProfileAnalogThresholdVALVE, XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE) \
_(XrHandJointsMotionRangeInfoEXT, XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT) \
@@ -3284,6 +4182,8 @@ XR_ENUM_STR(XrResult);
_(XrSystemMarkerTrackingPropertiesVARJO, XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_VARJO) \
_(XrEventDataMarkerTrackingUpdateVARJO, XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO) \
_(XrMarkerSpaceCreateInfoVARJO, XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO) \
+ _(XrFrameEndInfoML, XR_TYPE_FRAME_END_INFO_ML) \
+ _(XrGlobalDimmerFrameEndInfoML, XR_TYPE_GLOBAL_DIMMER_FRAME_END_INFO_ML) \
_(XrSpatialAnchorPersistenceInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT) \
_(XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT) \
_(XrSpaceQueryInfoFB, XR_TYPE_SPACE_QUERY_INFO_FB) \
@@ -3297,18 +4197,63 @@ XR_ENUM_STR(XrResult);
_(XrSpaceEraseInfoFB, XR_TYPE_SPACE_ERASE_INFO_FB) \
_(XrEventDataSpaceSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SAVE_COMPLETE_FB) \
_(XrEventDataSpaceEraseCompleteFB, XR_TYPE_EVENT_DATA_SPACE_ERASE_COMPLETE_FB) \
+ _(XrSpaceShareInfoFB, XR_TYPE_SPACE_SHARE_INFO_FB) \
+ _(XrEventDataSpaceShareCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SHARE_COMPLETE_FB) \
_(XrCompositionLayerSpaceWarpInfoFB, XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB) \
_(XrSystemSpaceWarpPropertiesFB, XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB) \
+ _(XrHapticAmplitudeEnvelopeVibrationFB, XR_TYPE_HAPTIC_AMPLITUDE_ENVELOPE_VIBRATION_FB) \
_(XrSemanticLabelsFB, XR_TYPE_SEMANTIC_LABELS_FB) \
_(XrRoomLayoutFB, XR_TYPE_ROOM_LAYOUT_FB) \
_(XrBoundary2DFB, XR_TYPE_BOUNDARY_2D_FB) \
+ _(XrSemanticLabelsSupportInfoFB, XR_TYPE_SEMANTIC_LABELS_SUPPORT_INFO_FB) \
_(XrDigitalLensControlALMALENCE, XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE) \
+ _(XrEventDataSceneCaptureCompleteFB, XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB) \
+ _(XrSceneCaptureRequestInfoFB, XR_TYPE_SCENE_CAPTURE_REQUEST_INFO_FB) \
_(XrSpaceContainerFB, XR_TYPE_SPACE_CONTAINER_FB) \
+ _(XrFoveationEyeTrackedProfileCreateInfoMETA, XR_TYPE_FOVEATION_EYE_TRACKED_PROFILE_CREATE_INFO_META) \
+ _(XrFoveationEyeTrackedStateMETA, XR_TYPE_FOVEATION_EYE_TRACKED_STATE_META) \
+ _(XrSystemFoveationEyeTrackedPropertiesMETA, XR_TYPE_SYSTEM_FOVEATION_EYE_TRACKED_PROPERTIES_META) \
+ _(XrSystemFaceTrackingPropertiesFB, XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_FB) \
+ _(XrFaceTrackerCreateInfoFB, XR_TYPE_FACE_TRACKER_CREATE_INFO_FB) \
+ _(XrFaceExpressionInfoFB, XR_TYPE_FACE_EXPRESSION_INFO_FB) \
+ _(XrFaceExpressionWeightsFB, XR_TYPE_FACE_EXPRESSION_WEIGHTS_FB) \
+ _(XrEyeTrackerCreateInfoFB, XR_TYPE_EYE_TRACKER_CREATE_INFO_FB) \
+ _(XrEyeGazesInfoFB, XR_TYPE_EYE_GAZES_INFO_FB) \
+ _(XrSystemEyeTrackingPropertiesFB, XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_FB) \
+ _(XrEyeGazesFB, XR_TYPE_EYE_GAZES_FB) \
_(XrPassthroughKeyboardHandsIntensityFB, XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB) \
_(XrCompositionLayerSettingsFB, XR_TYPE_COMPOSITION_LAYER_SETTINGS_FB) \
+ _(XrHapticPcmVibrationFB, XR_TYPE_HAPTIC_PCM_VIBRATION_FB) \
+ _(XrDevicePcmSampleRateStateFB, XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB) \
+ _(XrCompositionLayerDepthTestFB, XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_FB) \
+ _(XrLocalDimmingFrameEndInfoMETA, XR_TYPE_LOCAL_DIMMING_FRAME_END_INFO_META) \
+ _(XrSystemVirtualKeyboardPropertiesMETA, XR_TYPE_SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META) \
+ _(XrVirtualKeyboardCreateInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_CREATE_INFO_META) \
+ _(XrVirtualKeyboardSpaceCreateInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META) \
+ _(XrVirtualKeyboardLocationInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_LOCATION_INFO_META) \
+ _(XrVirtualKeyboardModelVisibilitySetInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_MODEL_VISIBILITY_SET_INFO_META) \
+ _(XrVirtualKeyboardAnimationStateMETA, XR_TYPE_VIRTUAL_KEYBOARD_ANIMATION_STATE_META) \
+ _(XrVirtualKeyboardModelAnimationStatesMETA, XR_TYPE_VIRTUAL_KEYBOARD_MODEL_ANIMATION_STATES_META) \
+ _(XrVirtualKeyboardTextureDataMETA, XR_TYPE_VIRTUAL_KEYBOARD_TEXTURE_DATA_META) \
+ _(XrVirtualKeyboardInputInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_INPUT_INFO_META) \
+ _(XrVirtualKeyboardTextContextChangeInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_TEXT_CONTEXT_CHANGE_INFO_META) \
+ _(XrEventDataVirtualKeyboardCommitTextMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_COMMIT_TEXT_META) \
+ _(XrEventDataVirtualKeyboardBackspaceMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_BACKSPACE_META) \
+ _(XrEventDataVirtualKeyboardEnterMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_ENTER_META) \
+ _(XrEventDataVirtualKeyboardShownMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_SHOWN_META) \
+ _(XrEventDataVirtualKeyboardHiddenMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_HIDDEN_META) \
+ _(XrExternalCameraOCULUS, XR_TYPE_EXTERNAL_CAMERA_OCULUS) \
_(XrPerformanceMetricsStateMETA, XR_TYPE_PERFORMANCE_METRICS_STATE_META) \
_(XrPerformanceMetricsCounterMETA, XR_TYPE_PERFORMANCE_METRICS_COUNTER_META) \
+ _(XrSpaceListSaveInfoFB, XR_TYPE_SPACE_LIST_SAVE_INFO_FB) \
+ _(XrEventDataSpaceListSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB) \
+ _(XrSpaceUserCreateInfoFB, XR_TYPE_SPACE_USER_CREATE_INFO_FB) \
_(XrSystemHeadsetIdPropertiesMETA, XR_TYPE_SYSTEM_HEADSET_ID_PROPERTIES_META) \
+ _(XrPassthroughColorLutCreateInfoMETA, XR_TYPE_PASSTHROUGH_COLOR_LUT_CREATE_INFO_META) \
+ _(XrPassthroughColorLutUpdateInfoMETA, XR_TYPE_PASSTHROUGH_COLOR_LUT_UPDATE_INFO_META) \
+ _(XrPassthroughColorMapLutMETA, XR_TYPE_PASSTHROUGH_COLOR_MAP_LUT_META) \
+ _(XrPassthroughColorMapInterpolatedLutMETA, XR_TYPE_PASSTHROUGH_COLOR_MAP_INTERPOLATED_LUT_META) \
+ _(XrSystemPassthroughColorLutPropertiesMETA, XR_TYPE_SYSTEM_PASSTHROUGH_COLOR_LUT_PROPERTIES_META) \
_(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \
_(XrPassthroughColorHTC, XR_TYPE_PASSTHROUGH_COLOR_HTC) \
_(XrPassthroughMeshTransformInfoHTC, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC) \
@@ -3317,6 +4262,17 @@ XR_ENUM_STR(XrResult);
_(XrFoveationDynamicModeInfoHTC, XR_TYPE_FOVEATION_DYNAMIC_MODE_INFO_HTC) \
_(XrFoveationCustomModeInfoHTC, XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC) \
_(XrActiveActionSetPrioritiesEXT, XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT) \
+ _(XrSystemForceFeedbackCurlPropertiesMNDX, XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX) \
+ _(XrForceFeedbackCurlApplyLocationsMNDX, XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX) \
+ _(XrHandTrackingDataSourceInfoEXT, XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT) \
+ _(XrHandTrackingDataSourceStateEXT, XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT) \
+ _(XrSystemPlaneDetectionPropertiesEXT, XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT) \
+ _(XrPlaneDetectorCreateInfoEXT, XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT) \
+ _(XrPlaneDetectorBeginInfoEXT, XR_TYPE_PLANE_DETECTOR_BEGIN_INFO_EXT) \
+ _(XrPlaneDetectorGetInfoEXT, XR_TYPE_PLANE_DETECTOR_GET_INFO_EXT) \
+ _(XrPlaneDetectorLocationEXT, XR_TYPE_PLANE_DETECTOR_LOCATION_EXT) \
+ _(XrPlaneDetectorLocationsEXT, XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT) \
+ _(XrPlaneDetectorPolygonBufferEXT, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT) \
#if defined(XR_USE_GRAPHICS_API_D3D11)
@@ -3458,6 +4414,16 @@ XR_ENUM_STR(XrResult);
#define XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_EGL(_)
#endif
+#if defined(XR_USE_PLATFORM_ML)
+/// Implementation detail of XR_LIST_STRUCTURE_TYPES()
+/// Structure types available only when XR_USE_PLATFORM_ML is defined
+#define XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_ML(_) \
+ _(XrCoordinateSpaceCreateInfoML, XR_TYPE_COORDINATE_SPACE_CREATE_INFO_ML) \
+
+#else
+#define XR_LIST_STRUCTURE_TYPES_XR_USE_PLATFORM_ML(_)
+#endif
+
#if defined(XR_USE_PLATFORM_WIN32)
/// Implementation detail of XR_LIST_STRUCTURE_TYPES()
/// Structure types available only when XR_USE_PLATFORM_WIN32 is defined
@@ -3521,6 +4487,7 @@ XR_ENUM_STR(XrResult);
_(XR_FB_android_surface_swapchain_create, 71) \
_(XR_FB_swapchain_update_state, 72) \
_(XR_FB_composition_layer_secure_content, 73) \
+ _(XR_FB_body_tracking, 77) \
_(XR_EXT_dpad_binding, 79) \
_(XR_VALVE_analog_threshold, 80) \
_(XR_EXT_hand_joints_motion_range, 81) \
@@ -3558,6 +4525,9 @@ XR_ENUM_STR(XrResult);
_(XR_VARJO_marker_tracking, 125) \
_(XR_VARJO_view_offset, 126) \
_(XR_ML_ml2_controller_interaction, 135) \
+ _(XR_ML_frame_end_info, 136) \
+ _(XR_ML_global_dimmer, 137) \
+ _(XR_ML_compat, 138) \
_(XR_MSFT_spatial_anchor_persistence, 143) \
_(XR_ULTRALEAP_hand_tracking_forearm, 150) \
_(XR_FB_spatial_entity_query, 157) \
@@ -3568,20 +4538,44 @@ XR_ENUM_STR(XrResult);
_(XR_FB_swapchain_update_state_opengl_es, 163) \
_(XR_FB_swapchain_update_state_vulkan, 164) \
_(XR_KHR_swapchain_usage_input_attachment_bit, 166) \
+ _(XR_FB_touch_controller_pro, 168) \
+ _(XR_FB_spatial_entity_sharing, 170) \
_(XR_FB_space_warp, 172) \
+ _(XR_FB_haptic_amplitude_envelope, 174) \
_(XR_FB_scene, 176) \
_(XR_EXT_palm_pose, 177) \
_(XR_ALMALENCE_digital_lens_control, 197) \
+ _(XR_FB_scene_capture, 199) \
_(XR_FB_spatial_entity_container, 200) \
+ _(XR_META_foveation_eye_tracked, 201) \
+ _(XR_FB_face_tracking, 202) \
+ _(XR_FB_eye_tracking_social, 203) \
_(XR_FB_passthrough_keyboard_hands, 204) \
_(XR_FB_composition_layer_settings, 205) \
+ _(XR_FB_touch_controller_proximity, 207) \
+ _(XR_FB_haptic_pcm, 210) \
+ _(XR_FB_composition_layer_depth_test, 213) \
+ _(XR_META_local_dimming, 217) \
+ _(XR_META_virtual_keyboard, 220) \
+ _(XR_OCULUS_external_camera, 227) \
_(XR_META_vulkan_swapchain_create_info, 228) \
_(XR_META_performance_metrics, 233) \
+ _(XR_FB_spatial_entity_storage_batch, 239) \
+ _(XR_FB_spatial_entity_user, 242) \
_(XR_META_headset_id, 246) \
+ _(XR_META_passthrough_color_lut, 267) \
_(XR_EXT_uuid, 300) \
+ _(XR_EXT_hand_interaction, 303) \
+ _(XR_QCOM_tracking_optimization_settings, 307) \
_(XR_HTC_passthrough, 318) \
_(XR_HTC_foveation, 319) \
_(XR_EXT_active_action_set_priority, 374) \
+ _(XR_MNDX_force_feedback_curl, 376) \
+ _(XR_BD_controller_interaction, 385) \
+ _(XR_EXT_local_floor, 427) \
+ _(XR_EXT_hand_tracking_data_source, 429) \
+ _(XR_EXT_plane_detection, 430) \
+ _(XR_OPPO_controller_interaction, 454) \
#endif
diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h
index 19b0e1c3f6..d0d05e97d0 100644
--- a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h
+++ b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h
@@ -2,7 +2,7 @@
#define OPENXR_REFLECTION_PARENT_STRUCTS_H_ 1
/*
-** Copyright (c) 2017-2022, The Khronos Group Inc.
+** Copyright (c) 2017-2023, The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -62,6 +62,8 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a
_avail(XrEventDataSpaceQueryCompleteFB, XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB) \
_avail(XrEventDataSpaceSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SAVE_COMPLETE_FB) \
_avail(XrEventDataSpaceEraseCompleteFB, XR_TYPE_EVENT_DATA_SPACE_ERASE_COMPLETE_FB) \
+ _avail(XrEventDataSpaceShareCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SHARE_COMPLETE_FB) \
+ _avail(XrEventDataSpaceListSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB) \
@@ -75,6 +77,8 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a
// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrHapticBaseHeader()
#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrHapticBaseHeader_CORE(_avail, _unavail) \
_avail(XrHapticVibration, XR_TYPE_HAPTIC_VIBRATION) \
+ _avail(XrHapticAmplitudeEnvelopeVibrationFB, XR_TYPE_HAPTIC_AMPLITUDE_ENVELOPE_VIBRATION_FB) \
+ _avail(XrHapticPcmVibrationFB, XR_TYPE_HAPTIC_PCM_VIBRATION_FB) \
diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h
index 300bbbad6d..ec186390a3 100644
--- a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h
+++ b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h
@@ -2,7 +2,7 @@
#define OPENXR_REFLECTION_STRUCTS_H_ 1
/*
-** Copyright (c) 2017-2022, The Khronos Group Inc.
+** Copyright (c) 2017-2023, The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -37,6 +37,7 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_VULKAN(_avail, _unavail) \
_impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_PLATFORM_ANDROID(_avail, _unavail) \
_impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_PLATFORM_EGL(_avail, _unavail) \
+ _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_PLATFORM_ML(_avail, _unavail) \
_impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_PLATFORM_WIN32(_avail, _unavail) \
@@ -143,6 +144,11 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_avail(XrCompositionLayerReprojectionInfoMSFT, XR_TYPE_COMPOSITION_LAYER_REPROJECTION_INFO_MSFT) \
_avail(XrCompositionLayerReprojectionPlaneOverrideMSFT, XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT) \
_avail(XrCompositionLayerSecureContentFB, XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB) \
+ _avail(XrSystemBodyTrackingPropertiesFB, XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_FB) \
+ _avail(XrBodyTrackerCreateInfoFB, XR_TYPE_BODY_TRACKER_CREATE_INFO_FB) \
+ _avail(XrBodySkeletonFB, XR_TYPE_BODY_SKELETON_FB) \
+ _avail(XrBodyJointsLocateInfoFB, XR_TYPE_BODY_JOINTS_LOCATE_INFO_FB) \
+ _avail(XrBodyJointLocationsFB, XR_TYPE_BODY_JOINT_LOCATIONS_FB) \
_avail(XrInteractionProfileDpadBindingEXT, XR_TYPE_INTERACTION_PROFILE_DPAD_BINDING_EXT) \
_avail(XrInteractionProfileAnalogThresholdVALVE, XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE) \
_avail(XrHandJointsMotionRangeInfoEXT, XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT) \
@@ -217,6 +223,8 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_avail(XrSystemMarkerTrackingPropertiesVARJO, XR_TYPE_SYSTEM_MARKER_TRACKING_PROPERTIES_VARJO) \
_avail(XrEventDataMarkerTrackingUpdateVARJO, XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO) \
_avail(XrMarkerSpaceCreateInfoVARJO, XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO) \
+ _avail(XrFrameEndInfoML, XR_TYPE_FRAME_END_INFO_ML) \
+ _avail(XrGlobalDimmerFrameEndInfoML, XR_TYPE_GLOBAL_DIMMER_FRAME_END_INFO_ML) \
_avail(XrSpatialAnchorPersistenceInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT) \
_avail(XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT) \
_avail(XrSpaceQueryInfoFB, XR_TYPE_SPACE_QUERY_INFO_FB) \
@@ -230,18 +238,63 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_avail(XrSpaceEraseInfoFB, XR_TYPE_SPACE_ERASE_INFO_FB) \
_avail(XrEventDataSpaceSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SAVE_COMPLETE_FB) \
_avail(XrEventDataSpaceEraseCompleteFB, XR_TYPE_EVENT_DATA_SPACE_ERASE_COMPLETE_FB) \
+ _avail(XrSpaceShareInfoFB, XR_TYPE_SPACE_SHARE_INFO_FB) \
+ _avail(XrEventDataSpaceShareCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SHARE_COMPLETE_FB) \
_avail(XrCompositionLayerSpaceWarpInfoFB, XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB) \
_avail(XrSystemSpaceWarpPropertiesFB, XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB) \
+ _avail(XrHapticAmplitudeEnvelopeVibrationFB, XR_TYPE_HAPTIC_AMPLITUDE_ENVELOPE_VIBRATION_FB) \
_avail(XrSemanticLabelsFB, XR_TYPE_SEMANTIC_LABELS_FB) \
_avail(XrRoomLayoutFB, XR_TYPE_ROOM_LAYOUT_FB) \
_avail(XrBoundary2DFB, XR_TYPE_BOUNDARY_2D_FB) \
+ _avail(XrSemanticLabelsSupportInfoFB, XR_TYPE_SEMANTIC_LABELS_SUPPORT_INFO_FB) \
_avail(XrDigitalLensControlALMALENCE, XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE) \
+ _avail(XrEventDataSceneCaptureCompleteFB, XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB) \
+ _avail(XrSceneCaptureRequestInfoFB, XR_TYPE_SCENE_CAPTURE_REQUEST_INFO_FB) \
_avail(XrSpaceContainerFB, XR_TYPE_SPACE_CONTAINER_FB) \
+ _avail(XrFoveationEyeTrackedProfileCreateInfoMETA, XR_TYPE_FOVEATION_EYE_TRACKED_PROFILE_CREATE_INFO_META) \
+ _avail(XrFoveationEyeTrackedStateMETA, XR_TYPE_FOVEATION_EYE_TRACKED_STATE_META) \
+ _avail(XrSystemFoveationEyeTrackedPropertiesMETA, XR_TYPE_SYSTEM_FOVEATION_EYE_TRACKED_PROPERTIES_META) \
+ _avail(XrSystemFaceTrackingPropertiesFB, XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_FB) \
+ _avail(XrFaceTrackerCreateInfoFB, XR_TYPE_FACE_TRACKER_CREATE_INFO_FB) \
+ _avail(XrFaceExpressionInfoFB, XR_TYPE_FACE_EXPRESSION_INFO_FB) \
+ _avail(XrFaceExpressionWeightsFB, XR_TYPE_FACE_EXPRESSION_WEIGHTS_FB) \
+ _avail(XrEyeTrackerCreateInfoFB, XR_TYPE_EYE_TRACKER_CREATE_INFO_FB) \
+ _avail(XrEyeGazesInfoFB, XR_TYPE_EYE_GAZES_INFO_FB) \
+ _avail(XrSystemEyeTrackingPropertiesFB, XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_FB) \
+ _avail(XrEyeGazesFB, XR_TYPE_EYE_GAZES_FB) \
_avail(XrPassthroughKeyboardHandsIntensityFB, XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB) \
_avail(XrCompositionLayerSettingsFB, XR_TYPE_COMPOSITION_LAYER_SETTINGS_FB) \
+ _avail(XrHapticPcmVibrationFB, XR_TYPE_HAPTIC_PCM_VIBRATION_FB) \
+ _avail(XrDevicePcmSampleRateStateFB, XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB) \
+ _avail(XrCompositionLayerDepthTestFB, XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_FB) \
+ _avail(XrLocalDimmingFrameEndInfoMETA, XR_TYPE_LOCAL_DIMMING_FRAME_END_INFO_META) \
+ _avail(XrSystemVirtualKeyboardPropertiesMETA, XR_TYPE_SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META) \
+ _avail(XrVirtualKeyboardCreateInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_CREATE_INFO_META) \
+ _avail(XrVirtualKeyboardSpaceCreateInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META) \
+ _avail(XrVirtualKeyboardLocationInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_LOCATION_INFO_META) \
+ _avail(XrVirtualKeyboardModelVisibilitySetInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_MODEL_VISIBILITY_SET_INFO_META) \
+ _avail(XrVirtualKeyboardAnimationStateMETA, XR_TYPE_VIRTUAL_KEYBOARD_ANIMATION_STATE_META) \
+ _avail(XrVirtualKeyboardModelAnimationStatesMETA, XR_TYPE_VIRTUAL_KEYBOARD_MODEL_ANIMATION_STATES_META) \
+ _avail(XrVirtualKeyboardTextureDataMETA, XR_TYPE_VIRTUAL_KEYBOARD_TEXTURE_DATA_META) \
+ _avail(XrVirtualKeyboardInputInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_INPUT_INFO_META) \
+ _avail(XrVirtualKeyboardTextContextChangeInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_TEXT_CONTEXT_CHANGE_INFO_META) \
+ _avail(XrEventDataVirtualKeyboardCommitTextMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_COMMIT_TEXT_META) \
+ _avail(XrEventDataVirtualKeyboardBackspaceMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_BACKSPACE_META) \
+ _avail(XrEventDataVirtualKeyboardEnterMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_ENTER_META) \
+ _avail(XrEventDataVirtualKeyboardShownMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_SHOWN_META) \
+ _avail(XrEventDataVirtualKeyboardHiddenMETA, XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_HIDDEN_META) \
+ _avail(XrExternalCameraOCULUS, XR_TYPE_EXTERNAL_CAMERA_OCULUS) \
_avail(XrPerformanceMetricsStateMETA, XR_TYPE_PERFORMANCE_METRICS_STATE_META) \
_avail(XrPerformanceMetricsCounterMETA, XR_TYPE_PERFORMANCE_METRICS_COUNTER_META) \
+ _avail(XrSpaceListSaveInfoFB, XR_TYPE_SPACE_LIST_SAVE_INFO_FB) \
+ _avail(XrEventDataSpaceListSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_LIST_SAVE_COMPLETE_FB) \
+ _avail(XrSpaceUserCreateInfoFB, XR_TYPE_SPACE_USER_CREATE_INFO_FB) \
_avail(XrSystemHeadsetIdPropertiesMETA, XR_TYPE_SYSTEM_HEADSET_ID_PROPERTIES_META) \
+ _avail(XrPassthroughColorLutCreateInfoMETA, XR_TYPE_PASSTHROUGH_COLOR_LUT_CREATE_INFO_META) \
+ _avail(XrPassthroughColorLutUpdateInfoMETA, XR_TYPE_PASSTHROUGH_COLOR_LUT_UPDATE_INFO_META) \
+ _avail(XrPassthroughColorMapLutMETA, XR_TYPE_PASSTHROUGH_COLOR_MAP_LUT_META) \
+ _avail(XrPassthroughColorMapInterpolatedLutMETA, XR_TYPE_PASSTHROUGH_COLOR_MAP_INTERPOLATED_LUT_META) \
+ _avail(XrSystemPassthroughColorLutPropertiesMETA, XR_TYPE_SYSTEM_PASSTHROUGH_COLOR_LUT_PROPERTIES_META) \
_avail(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \
_avail(XrPassthroughColorHTC, XR_TYPE_PASSTHROUGH_COLOR_HTC) \
_avail(XrPassthroughMeshTransformInfoHTC, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC) \
@@ -250,6 +303,17 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_avail(XrFoveationDynamicModeInfoHTC, XR_TYPE_FOVEATION_DYNAMIC_MODE_INFO_HTC) \
_avail(XrFoveationCustomModeInfoHTC, XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC) \
_avail(XrActiveActionSetPrioritiesEXT, XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT) \
+ _avail(XrSystemForceFeedbackCurlPropertiesMNDX, XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX) \
+ _avail(XrForceFeedbackCurlApplyLocationsMNDX, XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX) \
+ _avail(XrHandTrackingDataSourceInfoEXT, XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT) \
+ _avail(XrHandTrackingDataSourceStateEXT, XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT) \
+ _avail(XrSystemPlaneDetectionPropertiesEXT, XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT) \
+ _avail(XrPlaneDetectorCreateInfoEXT, XR_TYPE_PLANE_DETECTOR_CREATE_INFO_EXT) \
+ _avail(XrPlaneDetectorBeginInfoEXT, XR_TYPE_PLANE_DETECTOR_BEGIN_INFO_EXT) \
+ _avail(XrPlaneDetectorGetInfoEXT, XR_TYPE_PLANE_DETECTOR_GET_INFO_EXT) \
+ _avail(XrPlaneDetectorLocationEXT, XR_TYPE_PLANE_DETECTOR_LOCATION_EXT) \
+ _avail(XrPlaneDetectorLocationsEXT, XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT) \
+ _avail(XrPlaneDetectorPolygonBufferEXT, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT) \
#if defined(XR_USE_GRAPHICS_API_D3D11)
@@ -410,6 +474,16 @@ This file contains expansion macros (X Macros) for OpenXR structures.
#endif
+#if defined(XR_USE_PLATFORM_ML)
+#define _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_PLATFORM_ML(_avail, _unavail) \
+ _avail(XrCoordinateSpaceCreateInfoML, XR_TYPE_COORDINATE_SPACE_CREATE_INFO_ML) \
+
+#else
+#define _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_PLATFORM_ML(_avail, _unavail) \
+ _unavail(XrCoordinateSpaceCreateInfoML, XR_TYPE_COORDINATE_SPACE_CREATE_INFO_ML) \
+
+#endif
+
#if defined(XR_USE_PLATFORM_WIN32)
#define _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_PLATFORM_WIN32(_avail, _unavail) \
_avail(XrHolographicWindowAttachmentMSFT, XR_TYPE_HOLOGRAPHIC_WINDOW_ATTACHMENT_MSFT) \
diff --git a/thirdparty/openxr/patches/fix-gcc13-stdint.patch b/thirdparty/openxr/patches/fix-gcc13-stdint.patch
deleted file mode 100644
index 9e659eb210..0000000000
--- a/thirdparty/openxr/patches/fix-gcc13-stdint.patch
+++ /dev/null
@@ -1,12 +0,0 @@
-diff --git a/thirdparty/openxr/src/common/platform_utils.hpp b/thirdparty/openxr/src/common/platform_utils.hpp
-index 85d5cdab10..2d870cfea7 100644
---- a/thirdparty/openxr/src/common/platform_utils.hpp
-+++ b/thirdparty/openxr/src/common/platform_utils.hpp
-@@ -11,6 +11,7 @@
-
- #include "xr_dependencies.h"
- #include <string>
-+#include <stdint.h>
- #include <stdlib.h>
-
- // OpenXR paths and registry key locations
diff --git a/thirdparty/openxr/src/.clang-format b/thirdparty/openxr/src/.clang-format
deleted file mode 100644
index 36546cab92..0000000000
--- a/thirdparty/openxr/src/.clang-format
+++ /dev/null
@@ -1,10 +0,0 @@
----
-# Copyright (c) 2017-2022, The Khronos Group Inc.
-#
-# SPDX-License-Identifier: Apache-2.0
-# Use defaults from the Google style with the following exceptions:
-BasedOnStyle: Google
-IndentWidth: 4
-ColumnLimit: 132
-SortIncludes: false
-...
diff --git a/thirdparty/openxr/src/common/extra_algorithms.h b/thirdparty/openxr/src/common/extra_algorithms.h
index 64af4d08ff..eec429e12a 100644
--- a/thirdparty/openxr/src/common/extra_algorithms.h
+++ b/thirdparty/openxr/src/common/extra_algorithms.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// Copyright (c) 2019 Collabora, Ltd.
diff --git a/thirdparty/openxr/src/common/filesystem_utils.cpp b/thirdparty/openxr/src/common/filesystem_utils.cpp
index d3d4182fb9..16e6ff3292 100644
--- a/thirdparty/openxr/src/common/filesystem_utils.cpp
+++ b/thirdparty/openxr/src/common/filesystem_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/common/filesystem_utils.hpp b/thirdparty/openxr/src/common/filesystem_utils.hpp
index 4a5c987e7b..3dea1b2c3e 100644
--- a/thirdparty/openxr/src/common/filesystem_utils.hpp
+++ b/thirdparty/openxr/src/common/filesystem_utils.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/common/hex_and_handles.h b/thirdparty/openxr/src/common/hex_and_handles.h
index 341013d32b..300669033f 100644
--- a/thirdparty/openxr/src/common/hex_and_handles.h
+++ b/thirdparty/openxr/src/common/hex_and_handles.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// Copyright (c) 2019 Collabora, Ltd.
diff --git a/thirdparty/openxr/src/common/loader_interfaces.h b/thirdparty/openxr/src/common/loader_interfaces.h
index 9c74ed16f3..020c3456ea 100644
--- a/thirdparty/openxr/src/common/loader_interfaces.h
+++ b/thirdparty/openxr/src/common/loader_interfaces.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/common/object_info.cpp b/thirdparty/openxr/src/common/object_info.cpp
index 95b5aaf404..3f8f96bc6e 100644
--- a/thirdparty/openxr/src/common/object_info.cpp
+++ b/thirdparty/openxr/src/common/object_info.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// Copyright (c) 2019 Collabora, Ltd.
@@ -132,6 +132,8 @@ XrSdkSessionLabel::XrSdkSessionLabel(const XrDebugUtilsLabelEXT& label_info, boo
: label_name(label_info.labelName), debug_utils_label(label_info), is_individual_label(individual) {
// Update the c string pointer to the one we hold.
debug_utils_label.labelName = label_name.c_str();
+ // Zero out the next pointer to avoid a dangling pointer
+ debug_utils_label.next = nullptr;
}
XrSdkSessionLabelPtr XrSdkSessionLabel::make(const XrDebugUtilsLabelEXT& label_info, bool individual) {
@@ -143,7 +145,7 @@ void DebugUtilsData::AddObjectName(uint64_t object_handle, XrObjectType object_t
}
// We always want to remove the old individual label before we do anything else.
-// So, do that in it's own method
+// So, do that in its own method
void DebugUtilsData::RemoveIndividualLabel(XrSdkSessionLabelList& label_vec) {
if (!label_vec.empty() && label_vec.back()->is_individual_label) {
label_vec.pop_back();
diff --git a/thirdparty/openxr/src/common/object_info.h b/thirdparty/openxr/src/common/object_info.h
index 8e9742b605..247ede0dcc 100644
--- a/thirdparty/openxr/src/common/object_info.h
+++ b/thirdparty/openxr/src/common/object_info.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// Copyright (c) 2019 Collabora, Ltd.
diff --git a/thirdparty/openxr/src/common/platform_utils.hpp b/thirdparty/openxr/src/common/platform_utils.hpp
index 2d870cfea7..219d19789d 100644
--- a/thirdparty/openxr/src/common/platform_utils.hpp
+++ b/thirdparty/openxr/src/common/platform_utils.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
@@ -37,6 +37,10 @@
#include "common_config.h"
#endif // OPENXR_HAVE_COMMON_CONFIG
+// Consumers of this file must ensure this function is implemented. For example, the loader will implement this function so that it
+// can route messages through the loader's logging system.
+void LogPlatformUtilsError(const std::string& message);
+
// Environment variables
#if defined(XR_OS_LINUX) || defined(XR_OS_APPLE)
@@ -56,9 +60,11 @@ static inline char* ImplGetSecureEnv(const char* name) {
#elif defined(HAVE___SECURE_GETENV)
return __secure_getenv(name);
#else
+// clang-format off
#pragma message( \
"Warning: Falling back to non-secure getenv for environmental" \
"lookups! Consider updating to a different libc.")
+ // clang-format on
return ImplGetEnv(name);
#endif
@@ -79,6 +85,12 @@ static inline std::string PlatformUtilsGetEnv(const char* name) {
static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
auto str = detail::ImplGetSecureEnv(name);
if (str == nullptr) {
+ str = detail::ImplGetEnv(name);
+ if (str != nullptr && !std::string(str).empty()) {
+ LogPlatformUtilsError(std::string("!!! WARNING !!! Environment variable ") + name +
+ " is being ignored due to running with secure execution. The value '" + str +
+ "' will NOT be used.");
+ }
return {};
}
return str;
@@ -131,12 +143,6 @@ static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std:
#elif defined(XR_OS_WINDOWS)
-#if !defined(NDEBUG)
-inline void LogError(const std::string& error) { OutputDebugStringA(error.c_str()); }
-#else
-#define LogError(x)
-#endif
-
inline std::wstring utf8_to_wide(const std::string& utf8Text) {
if (utf8Text.empty()) {
return {};
@@ -145,7 +151,7 @@ inline std::wstring utf8_to_wide(const std::string& utf8Text) {
std::wstring wideText;
const int wideLength = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), nullptr, 0);
if (wideLength == 0) {
- LogError("utf8_to_wide get size error: " + std::to_string(::GetLastError()));
+ LogPlatformUtilsError("utf8_to_wide get size error: " + std::to_string(::GetLastError()));
return {};
}
@@ -154,7 +160,7 @@ inline std::wstring utf8_to_wide(const std::string& utf8Text) {
wchar_t* wideString = const_cast<wchar_t*>(wideText.data()); // mutable data() only exists in c++17
const int length = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), wideString, wideLength);
if (length != wideLength) {
- LogError("utf8_to_wide convert string error: " + std::to_string(::GetLastError()));
+ LogPlatformUtilsError("utf8_to_wide convert string error: " + std::to_string(::GetLastError()));
return {};
}
@@ -169,7 +175,7 @@ inline std::string wide_to_utf8(const std::wstring& wideText) {
std::string narrowText;
int narrowLength = ::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), nullptr, 0, nullptr, nullptr);
if (narrowLength == 0) {
- LogError("wide_to_utf8 get size error: " + std::to_string(::GetLastError()));
+ LogPlatformUtilsError("wide_to_utf8 get size error: " + std::to_string(::GetLastError()));
return {};
}
@@ -179,7 +185,7 @@ inline std::string wide_to_utf8(const std::wstring& wideText) {
const int length =
::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), narrowString, narrowLength, nullptr, nullptr);
if (length != narrowLength) {
- LogError("wide_to_utf8 convert string error: " + std::to_string(::GetLastError()));
+ LogPlatformUtilsError("wide_to_utf8 convert string error: " + std::to_string(::GetLastError()));
return {};
}
@@ -245,7 +251,7 @@ static inline std::string PlatformUtilsGetEnv(const char* name) {
// call if there was enough capacity. Else it returns the required capacity (including null terminator).
const DWORD length = ::GetEnvironmentVariableW(wname.c_str(), wValueData, (DWORD)wValue.size());
if ((length == 0) || (length >= wValue.size())) { // If error or the variable increased length between calls...
- LogError("GetEnvironmentVariable get value error: " + std::to_string(::GetLastError()));
+ LogPlatformUtilsError("GetEnvironmentVariable get value error: " + std::to_string(::GetLastError()));
return {};
}
@@ -256,13 +262,20 @@ static inline std::string PlatformUtilsGetEnv(const char* name) {
// Acts the same as PlatformUtilsGetEnv except returns an empty string if IsHighIntegrityLevel.
static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
+ // No secure version for Windows so the below integrity check is needed.
+ const std::string envValue = PlatformUtilsGetEnv(name);
+
// Do not allow high integrity processes to act on data that can be controlled by medium integrity processes.
if (IsHighIntegrityLevel()) {
+ if (!envValue.empty()) {
+ LogPlatformUtilsError(std::string("!!! WARNING !!! Environment variable ") + name +
+ " is being ignored due to running from an elevated context. The value '" + envValue +
+ "' will NOT be used.");
+ }
return {};
}
- // No secure version for Windows so the above integrity check is needed.
- return PlatformUtilsGetEnv(name);
+ return envValue;
}
// Sets an environment variable via UTF8 strings.
@@ -303,7 +316,7 @@ static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* va
// Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {
// Prefix for the runtime JSON file name
- static const char* rt_dir_prefixes[] = {"/oem", "/vendor", "/system"};
+ static const char* rt_dir_prefixes[] = {"/product", "/odm", "/oem", "/vendor", "/system"};
static const std::string rt_filename = "/active_runtime.json";
static const std::string subdir = "/etc/openxr/";
for (const auto prefix : rt_dir_prefixes) {
diff --git a/thirdparty/openxr/src/common/stdfs_conditions.h b/thirdparty/openxr/src/common/stdfs_conditions.h
index 6dc18cc620..0a551f08cd 100644
--- a/thirdparty/openxr/src/common/stdfs_conditions.h
+++ b/thirdparty/openxr/src/common/stdfs_conditions.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/common/unique_asset.h b/thirdparty/openxr/src/common/unique_asset.h
index 4929039a03..a8ae8077bc 100644
--- a/thirdparty/openxr/src/common/unique_asset.h
+++ b/thirdparty/openxr/src/common/unique_asset.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
#pragma once
diff --git a/thirdparty/openxr/src/common/vulkan_debug_object_namer.hpp b/thirdparty/openxr/src/common/vulkan_debug_object_namer.hpp
new file mode 100644
index 0000000000..451219d20f
--- /dev/null
+++ b/thirdparty/openxr/src/common/vulkan_debug_object_namer.hpp
@@ -0,0 +1,63 @@
+// Copyright (c) 2017-2023, The Khronos Group Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#ifdef XR_USE_GRAPHICS_API_VULKAN
+
+#include <vulkan/vulkan_core.h>
+#include <stdexcept>
+
+/// Utility class for assigning debug names to Vulkan objects we create.
+class VulkanDebugObjectNamer {
+ public:
+ /// Construct without initializing
+ VulkanDebugObjectNamer() = default;
+
+ /// Construct and initialize
+ VulkanDebugObjectNamer(VkInstance instance, VkDevice device) : m_vkDevice{device} {
+ vkSetDebugUtilsObjectNameEXT =
+ (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(instance, "vkSetDebugUtilsObjectNameEXT");
+ }
+ /// Copy constructor
+ VulkanDebugObjectNamer(const VulkanDebugObjectNamer&) = default;
+ /// Copy assignment operator
+ VulkanDebugObjectNamer& operator=(const VulkanDebugObjectNamer&) = default;
+
+ /// Destructor
+ ~VulkanDebugObjectNamer() { Reset(); }
+
+ /// (Re-) Initialize the namer: takes a valid `VkInstance` and `VkDevice`
+ void Init(VkInstance instance, VkDevice device) {
+ Reset();
+ *this = VulkanDebugObjectNamer(instance, device);
+ }
+
+ /// The main operation of the namer: actually set an object name.
+ ///
+ /// If the namer is not initialized, this exits silently.
+ VkResult SetName(VkObjectType objectType, uint64_t objectHandle, const char* pObjectName) const {
+ if (m_vkDevice == nullptr) {
+ return VK_SUCCESS;
+ }
+ if (vkSetDebugUtilsObjectNameEXT != nullptr) {
+ VkDebugUtilsObjectNameInfoEXT nameInfo{VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, nullptr, objectType,
+ objectHandle, pObjectName};
+ return vkSetDebugUtilsObjectNameEXT(m_vkDevice, &nameInfo);
+ }
+ return VK_SUCCESS;
+ }
+
+ /// De-initialize the namer, forgetting the device and the function pointer loaded from the instance.
+ void Reset() {
+ vkSetDebugUtilsObjectNameEXT = nullptr;
+ m_vkDevice = VK_NULL_HANDLE;
+ }
+
+ private:
+ VkDevice m_vkDevice{VK_NULL_HANDLE};
+ PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{nullptr};
+};
+
+#endif
diff --git a/thirdparty/openxr/src/common/xr_dependencies.h b/thirdparty/openxr/src/common/xr_dependencies.h
index e34527abc3..5c7bd04774 100644
--- a/thirdparty/openxr/src/common/xr_dependencies.h
+++ b/thirdparty/openxr/src/common/xr_dependencies.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2022, The Khronos Group Inc.
+// Copyright (c) 2018-2023, The Khronos Group Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
@@ -46,18 +46,6 @@
#ifdef XR_USE_PLATFORM_XLIB
#include <X11/Xlib.h>
#include <X11/Xutil.h>
-
-#ifdef Success
-#undef Success
-#endif // Success
-
-#ifdef Always
-#undef Always
-#endif // Always
-
-#ifdef None
-#undef None
-#endif // None
#endif // XR_USE_PLATFORM_XLIB
#ifdef XR_USE_PLATFORM_XCB
@@ -72,7 +60,7 @@
#include <xcb/glx.h>
#endif // XR_USE_PLATFORM_XCB
#ifdef XR_USE_PLATFORM_MACOS
-#include <CL/cl_gl_ext.h>
+#include <OpenCL/cl_gl_ext.h>
#endif // XR_USE_PLATFORM_MACOS
#endif // XR_USE_GRAPHICS_API_OPENGL
@@ -87,3 +75,19 @@
#ifdef XR_USE_PLATFORM_WAYLAND
#include "wayland-client.h"
#endif // XR_USE_PLATFORM_WAYLAND
+
+#ifdef XR_USE_GRAPHICS_API_OPENGL
+#if defined(XR_USE_PLATFORM_XLIB) || defined(XR_USE_PLATFORM_XCB)
+#ifdef Success
+#undef Success
+#endif // Success
+
+#ifdef Always
+#undef Always
+#endif // Always
+
+#ifdef None
+#undef None
+#endif // None
+#endif // defined(XR_USE_PLATFORM_XLIB) || defined(XR_USE_PLATFORM_XCB)
+#endif // XR_USE_GRAPHICS_API_OPENGL
diff --git a/thirdparty/openxr/src/common/xr_linear.h b/thirdparty/openxr/src/common/xr_linear.h
index 1f0e803b7a..5b0da645ac 100644
--- a/thirdparty/openxr/src/common/xr_linear.h
+++ b/thirdparty/openxr/src/common/xr_linear.h
@@ -21,11 +21,6 @@
#ifndef XR_LINEAR_H_
#define XR_LINEAR_H_
-#if defined(OS_LINUX_XCB) || defined(OS_LINUX_XCB_GLX) || defined(OS_LINUX_WAYLAND)
-#pragma GCC diagnostic ignored "-Wunused-function"
-#pragma clang diagnostic ignored "-Wunused-function"
-#endif
-
#include <openxr/openxr.h>
/*
@@ -51,6 +46,7 @@ XrVector2f
XrVector3f
XrVector4f
XrQuaternionf
+XrPosef
XrMatrix4x4f
inline static void XrVector3f_Set(XrVector3f* v, const float value);
@@ -64,8 +60,18 @@ inline static void XrVector3f_Scale(XrVector3f* result, const XrVector3f* a, con
inline static void XrVector3f_Normalize(XrVector3f* v);
inline static float XrVector3f_Length(const XrVector3f* v);
+inline static void XrQuaternionf_CreateIdentity(XrQuaternionf* q);
+inline static void XrQuaternionf_CreateFromAxisAngle(XrQuaternionf* result, const XrVector3f* axis, const float angleInRadians);
inline static void XrQuaternionf_Lerp(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b, const float fraction);
-inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b;
+inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b);
+inline static void XrQuaternionf_Invert(XrQuaternionf* result, const XrQuaternionf* q);
+inline static void XrQuaternionf_Normalize(XrQuaternionf* q);
+inline static void XrQuaternionf_RotateVector3f(XrVector3f* result, const XrQuaternionf* a, const XrVector3f* v);
+
+inline static void XrPosef_CreateIdentity(XrPosef* result);
+inline static void XrPosef_TransformVector3f(XrVector3f* result, const XrPosef* a, const XrVector3f* v);
+inline static void XrPosef_Multiply(XrPosef* result, const XrPosef* a, const XrPosef* b);
+inline static void XrPosef_Invert(XrPosef* result, const XrPosef* a);
inline static void XrMatrix4x4f_CreateIdentity(XrMatrix4x4f* result);
inline static void XrMatrix4x4f_CreateTranslation(XrMatrix4x4f* result, const float x, const float y, const float z);
@@ -74,13 +80,13 @@ inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float
inline static void XrMatrix4x4f_CreateScale(XrMatrix4x4f* result, const float x, const float y, const float z);
inline static void XrMatrix4x4f_CreateTranslationRotationScale(XrMatrix4x4f* result, const XrVector3f* translation,
const XrQuaternionf* rotation, const XrVector3f* scale);
-inline static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f* result, const float tanAngleLeft, const float tanAngleRight,
- const float tanAngleUp, float const tanAngleDown, const float nearZ,
- const float farZ);
-inline static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f* result, const float fovDegreesLeft, const float fovDegreesRight,
- const float fovDegreeUp, const float fovDegreesDown, const float nearZ,
- const float farZ);
-inline static void XrMatrix4x4f_CreateFromQuaternion(XrMatrix4x4f* result, const XrQuaternionf* src);
+inline static void XrMatrix4x4f_CreateFromRigidTransform(XrMatrix4x4f* result, const XrPosef* s);
+inline static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const float tanAngleLeft,
+ const float tanAngleRight, const float tanAngleUp, float const tanAngleDown,
+ const float nearZ, const float farZ);
+inline static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const XrFovf fov,
+ const float nearZ, const float farZ);
+inline static void XrMatrix4x4f_CreateFromQuaternion(XrMatrix4x4f* result, const XrQuaternionf* quat);
inline static void XrMatrix4x4f_CreateOffsetScaleForBounds(XrMatrix4x4f* result, const XrMatrix4x4f* matrix, const XrVector3f* mins,
const XrVector3f* maxs);
@@ -207,6 +213,13 @@ inline static void XrVector3f_Normalize(XrVector3f* v) {
inline static float XrVector3f_Length(const XrVector3f* v) { return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z); }
+inline static void XrQuaternionf_CreateIdentity(XrQuaternionf* q) {
+ q->x = 0.0f;
+ q->y = 0.0f;
+ q->z = 0.0f;
+ q->w = 1.0f;
+}
+
inline static void XrQuaternionf_CreateFromAxisAngle(XrQuaternionf* result, const XrVector3f* axis, const float angleInRadians) {
float s = sinf(angleInRadians / 2.0f);
float lengthRcp = XrRcpSqrt(axis->x * axis->x + axis->y * axis->y + axis->z * axis->z);
@@ -238,6 +251,58 @@ inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuatern
result->w = (b->w * a->w) - (b->x * a->x) - (b->y * a->y) - (b->z * a->z);
}
+inline static void XrQuaternionf_Invert(XrQuaternionf* result, const XrQuaternionf* q) {
+ result->x = -q->x;
+ result->y = -q->y;
+ result->z = -q->z;
+ result->w = q->w;
+}
+
+inline static void XrQuaternionf_Normalize(XrQuaternionf* q) {
+ const float lengthRcp = XrRcpSqrt(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w);
+ q->x *= lengthRcp;
+ q->y *= lengthRcp;
+ q->z *= lengthRcp;
+ q->w *= lengthRcp;
+}
+
+inline static void XrQuaternionf_RotateVector3f(XrVector3f* result, const XrQuaternionf* a, const XrVector3f* v) {
+ XrQuaternionf q = {v->x, v->y, v->z, 0.0f};
+ XrQuaternionf aq;
+ XrQuaternionf_Multiply(&aq, &q, a);
+ XrQuaternionf aInv;
+ XrQuaternionf_Invert(&aInv, a);
+ XrQuaternionf aqaInv;
+ XrQuaternionf_Multiply(&aqaInv, &aInv, &aq);
+
+ result->x = aqaInv.x;
+ result->y = aqaInv.y;
+ result->z = aqaInv.z;
+}
+
+inline static void XrPosef_CreateIdentity(XrPosef* result) {
+ XrQuaternionf_CreateIdentity(&result->orientation);
+ XrVector3f_Set(&result->position, 0);
+}
+
+inline static void XrPosef_TransformVector3f(XrVector3f* result, const XrPosef* a, const XrVector3f* v) {
+ XrVector3f r0;
+ XrQuaternionf_RotateVector3f(&r0, &a->orientation, v);
+ XrVector3f_Add(result, &r0, &a->position);
+}
+
+inline static void XrPosef_Multiply(XrPosef* result, const XrPosef* a, const XrPosef* b) {
+ XrQuaternionf_Multiply(&result->orientation, &b->orientation, &a->orientation);
+ XrPosef_TransformVector3f(&result->position, a, &b->position);
+}
+
+inline static void XrPosef_Invert(XrPosef* result, const XrPosef* a) {
+ XrQuaternionf_Invert(&result->orientation, &a->orientation);
+ XrVector3f aPosNeg;
+ XrVector3f_Scale(&aPosNeg, &a->position, -1.0f);
+ XrQuaternionf_RotateVector3f(&result->position, &result->orientation, &aPosNeg);
+}
+
// Use left-multiplication to accumulate transformations.
inline static void XrMatrix4x4f_Multiply(XrMatrix4x4f* result, const XrMatrix4x4f* a, const XrMatrix4x4f* b) {
result->m[0] = a->m[0] * b->m[0] + a->m[4] * b->m[1] + a->m[8] * b->m[2] + a->m[12] * b->m[3];
@@ -379,23 +444,31 @@ inline static void XrMatrix4x4f_CreateTranslation(XrMatrix4x4f* result, const fl
}
// Creates a rotation matrix.
-// If -Z=forward, +Y=up, +X=right, then degreesX=pitch, degreesY=yaw, degreesZ=roll.
-inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float degreesX, const float degreesY,
- const float degreesZ) {
- const float sinX = sinf(degreesX * (MATH_PI / 180.0f));
- const float cosX = cosf(degreesX * (MATH_PI / 180.0f));
+// If -Z=forward, +Y=up, +X=right, then radiansX=pitch, radiansY=yaw, radiansZ=roll.
+inline static void XrMatrix4x4f_CreateRotationRadians(XrMatrix4x4f* result, const float radiansX, const float radiansY,
+ const float radiansZ) {
+ const float sinX = sinf(radiansX);
+ const float cosX = cosf(radiansX);
const XrMatrix4x4f rotationX = {{1, 0, 0, 0, 0, cosX, sinX, 0, 0, -sinX, cosX, 0, 0, 0, 0, 1}};
- const float sinY = sinf(degreesY * (MATH_PI / 180.0f));
- const float cosY = cosf(degreesY * (MATH_PI / 180.0f));
+ const float sinY = sinf(radiansY);
+ const float cosY = cosf(radiansY);
const XrMatrix4x4f rotationY = {{cosY, 0, -sinY, 0, 0, 1, 0, 0, sinY, 0, cosY, 0, 0, 0, 0, 1}};
- const float sinZ = sinf(degreesZ * (MATH_PI / 180.0f));
- const float cosZ = cosf(degreesZ * (MATH_PI / 180.0f));
+ const float sinZ = sinf(radiansZ);
+ const float cosZ = cosf(radiansZ);
const XrMatrix4x4f rotationZ = {{cosZ, sinZ, 0, 0, -sinZ, cosZ, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}};
XrMatrix4x4f rotationXY;
XrMatrix4x4f_Multiply(&rotationXY, &rotationY, &rotationX);
XrMatrix4x4f_Multiply(result, &rotationZ, &rotationXY);
}
+// Creates a rotation matrix.
+// If -Z=forward, +Y=up, +X=right, then degreesX=pitch, degreesY=yaw, degreesZ=roll.
+inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float degreesX, const float degreesY,
+ const float degreesZ) {
+ XrMatrix4x4f_CreateRotationRadians(result, degreesX * (MATH_PI / 180.0f), degreesY * (MATH_PI / 180.0f),
+ degreesZ * (MATH_PI / 180.0f));
+}
+
// Creates a scale matrix.
inline static void XrMatrix4x4f_CreateScale(XrMatrix4x4f* result, const float x, const float y, const float z) {
result->m[0] = x;
@@ -471,6 +544,11 @@ inline static void XrMatrix4x4f_CreateTranslationRotationScale(XrMatrix4x4f* res
XrMatrix4x4f_Multiply(result, &translationMatrix, &combinedMatrix);
}
+inline static void XrMatrix4x4f_CreateFromRigidTransform(XrMatrix4x4f* result, const XrPosef* s) {
+ const XrVector3f identityScale = {1.0f, 1.0f, 1.0f};
+ XrMatrix4x4f_CreateTranslationRotationScale(result, &s->position, &s->orientation, &identityScale);
+}
+
// Creates a projection matrix based on the specified dimensions.
// The projection matrix transforms -Z=forward, +Y=up, +X=right to the appropriate clip space for the graphics API.
// The far plane is placed at infinity if farZ <= nearZ.
diff --git a/thirdparty/openxr/src/loader/android_utilities.cpp b/thirdparty/openxr/src/loader/android_utilities.cpp
index 59d9a99b74..9a3ad76ce0 100644
--- a/thirdparty/openxr/src/loader/android_utilities.cpp
+++ b/thirdparty/openxr/src/loader/android_utilities.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2022, The Khronos Group Inc.
+// Copyright (c) 2020-2023, The Khronos Group Inc.
// Copyright (c) 2020-2021, Collabora, Ltd.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
@@ -19,10 +19,10 @@
#include <vector>
#include <android/log.h>
-#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "openxr_loader", __VA_ARGS__)
-#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, "openxr_loader", __VA_ARGS__)
-#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "openxr_loader", __VA_ARGS__)
-#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, "openxr_loader", __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, "OpenXR-Loader", __VA_ARGS__)
+#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, "OpenXR-Loader", __VA_ARGS__)
+#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "OpenXR-Loader", __VA_ARGS__)
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, "OpenXR-Loader", __VA_ARGS__)
namespace openxr_android {
using wrap::android::content::ContentUris;
diff --git a/thirdparty/openxr/src/loader/android_utilities.h b/thirdparty/openxr/src/loader/android_utilities.h
index adb8abaf1f..f66c9bf1d0 100644
--- a/thirdparty/openxr/src/loader/android_utilities.h
+++ b/thirdparty/openxr/src/loader/android_utilities.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2022, The Khronos Group Inc.
+// Copyright (c) 2020-2023, The Khronos Group Inc.
// Copyright (c) 2020-2021, Collabora, Ltd.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
diff --git a/thirdparty/openxr/src/loader/api_layer_interface.cpp b/thirdparty/openxr/src/loader/api_layer_interface.cpp
index b946e09402..5560c31a52 100644
--- a/thirdparty/openxr/src/loader/api_layer_interface.cpp
+++ b/thirdparty/openxr/src/loader/api_layer_interface.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
@@ -82,6 +82,12 @@ XrResult ApiLayerInterface::GetApiLayerProperties(const std::string& openxr_comm
return result;
}
+ // check for potential overflow before static_cast<uint32_t>
+ if (manifest_files.size() >= UINT32_MAX) {
+ LoaderLogger::LogErrorMessage(openxr_command, "ApiLayerInterface::GetApiLayerProperties - too many API layers found");
+ return XR_ERROR_RUNTIME_FAILURE;
+ }
+
manifest_count = static_cast<uint32_t>(manifest_files.size());
if (nullptr == outgoing_count) {
LoaderLogger::LogErrorMessage("xrEnumerateInstanceExtensionProperties",
@@ -131,8 +137,8 @@ XrResult ApiLayerInterface::GetInstanceExtensionProperties(const std::string& op
}
bool found = false;
- auto num_files = static_cast<uint32_t>(manifest_files.size());
- for (uint32_t man_file = 0; man_file < num_files; ++man_file) {
+ size_t num_files = manifest_files.size();
+ for (size_t man_file = 0; man_file < num_files; ++man_file) {
// If a layer with the provided name exists, get it's instance extension information.
if (manifest_files[man_file]->LayerName() == layer_name) {
manifest_files[man_file]->GetInstanceExtensionProperties(extension_properties);
@@ -172,8 +178,8 @@ XrResult ApiLayerInterface::GetInstanceExtensionProperties(const std::string& op
}
// Grab the layer instance extensions information
- auto num_files = static_cast<uint32_t>(manifest_files.size());
- for (uint32_t man_file = 0; man_file < num_files; ++man_file) {
+ size_t num_files = manifest_files.size();
+ for (size_t man_file = 0; man_file < num_files; ++man_file) {
manifest_files[man_file]->GetInstanceExtensionProperties(extension_properties);
}
}
diff --git a/thirdparty/openxr/src/loader/api_layer_interface.hpp b/thirdparty/openxr/src/loader/api_layer_interface.hpp
index b93e44584e..98685b0c32 100644
--- a/thirdparty/openxr/src/loader/api_layer_interface.hpp
+++ b/thirdparty/openxr/src/loader/api_layer_interface.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/loader/exception_handling.hpp b/thirdparty/openxr/src/loader/exception_handling.hpp
index 428dd00279..bc0d9b65e3 100644
--- a/thirdparty/openxr/src/loader/exception_handling.hpp
+++ b/thirdparty/openxr/src/loader/exception_handling.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2022, The Khronos Group Inc.
+// Copyright (c) 2019-2023, The Khronos Group Inc.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
diff --git a/thirdparty/openxr/src/loader/loader_core.cpp b/thirdparty/openxr/src/loader/loader_core.cpp
index f2bc87d1fa..98d3fa971a 100644
--- a/thirdparty/openxr/src/loader/loader_core.cpp
+++ b/thirdparty/openxr/src/loader/loader_core.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/loader/loader_instance.cpp b/thirdparty/openxr/src/loader/loader_instance.cpp
index b24c8de53b..badd39193c 100644
--- a/thirdparty/openxr/src/loader/loader_instance.cpp
+++ b/thirdparty/openxr/src/loader/loader_instance.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
@@ -200,8 +200,8 @@ XrResult LoaderInstance::CreateInstance(PFN_xrGetInstanceProcAddr get_instance_p
if (!api_layer_interfaces.empty()) {
// Initialize an array of ApiLayerNextInfo structs
std::unique_ptr<XrApiLayerNextInfo[]> next_info_list(new XrApiLayerNextInfo[api_layer_interfaces.size()]);
- auto ni_index = static_cast<uint32_t>(api_layer_interfaces.size() - 1);
- for (uint32_t i = 0; i <= ni_index; i++) {
+ size_t ni_index = api_layer_interfaces.size() - 1;
+ for (size_t i = 0; i <= ni_index; i++) {
next_info_list[i].structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO;
next_info_list[i].structVersion = XR_API_LAYER_NEXT_INFO_STRUCT_VERSION;
next_info_list[i].structSize = sizeof(XrApiLayerNextInfo);
diff --git a/thirdparty/openxr/src/loader/loader_instance.hpp b/thirdparty/openxr/src/loader/loader_instance.hpp
index 1d43ed758d..a0268a855c 100644
--- a/thirdparty/openxr/src/loader/loader_instance.hpp
+++ b/thirdparty/openxr/src/loader/loader_instance.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/loader/loader_logger.cpp b/thirdparty/openxr/src/loader/loader_logger.cpp
index dba46aa92d..1c8d64f394 100644
--- a/thirdparty/openxr/src/loader/loader_logger.cpp
+++ b/thirdparty/openxr/src/loader/loader_logger.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
@@ -26,6 +26,9 @@
#include <utility>
#include <vector>
+// For routing platform_utils.hpp messages into the LoaderLogger.
+void LogPlatformUtilsError(const std::string& message) { LoaderLogger::LogErrorMessage("platform_utils", message); }
+
bool LoaderLogRecorder::LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT /*message_severity*/,
XrDebugUtilsMessageTypeFlagsEXT /*message_type*/,
const XrDebugUtilsMessengerCallbackDataEXT* /*callback_data*/) {
diff --git a/thirdparty/openxr/src/loader/loader_logger.hpp b/thirdparty/openxr/src/loader/loader_logger.hpp
index 260ebe354a..d31fac093a 100644
--- a/thirdparty/openxr/src/loader/loader_logger.hpp
+++ b/thirdparty/openxr/src/loader/loader_logger.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/loader/loader_logger_recorders.cpp b/thirdparty/openxr/src/loader/loader_logger_recorders.cpp
index 7673678c60..32e4687b2f 100644
--- a/thirdparty/openxr/src/loader/loader_logger_recorders.cpp
+++ b/thirdparty/openxr/src/loader/loader_logger_recorders.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
@@ -160,16 +160,16 @@ bool DebugUtilsLogRecorder::LogMessage(XrLoaderLogMessageSeverityFlagBits messag
XrDebugUtilsMessageTypeFlagsEXT utils_type = LoaderLogMessageTypesToDebugUtilsMessageTypes(message_type);
// Convert the loader log message into the debug utils log message information
- XrDebugUtilsMessengerCallbackDataEXT utils_callback_data = {};
+ XrDebugUtilsMessengerCallbackDataEXT utils_callback_data{};
utils_callback_data.type = XR_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT;
utils_callback_data.messageId = callback_data->message_id;
utils_callback_data.functionName = callback_data->command_name;
utils_callback_data.message = callback_data->message;
- std::vector<XrDebugUtilsObjectNameInfoEXT> utils_objects;
- utils_objects.resize(callback_data->object_count);
+
+ XrDebugUtilsObjectNameInfoEXT example_utils_info{};
+ example_utils_info.type = XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
+ std::vector<XrDebugUtilsObjectNameInfoEXT> utils_objects(callback_data->object_count, example_utils_info);
for (uint8_t object = 0; object < callback_data->object_count; ++object) {
- utils_objects[object].type = XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
- utils_objects[object].next = nullptr;
utils_objects[object].objectHandle = callback_data->objects[object].handle;
utils_objects[object].objectType = callback_data->objects[object].type;
utils_objects[object].objectName = callback_data->objects[object].name.c_str();
diff --git a/thirdparty/openxr/src/loader/loader_logger_recorders.hpp b/thirdparty/openxr/src/loader/loader_logger_recorders.hpp
index 31e5243c45..7b934202d5 100644
--- a/thirdparty/openxr/src/loader/loader_logger_recorders.hpp
+++ b/thirdparty/openxr/src/loader/loader_logger_recorders.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/loader/loader_platform.hpp b/thirdparty/openxr/src/loader/loader_platform.hpp
index e2757fffb9..0ea80c05b8 100644
--- a/thirdparty/openxr/src/loader/loader_platform.hpp
+++ b/thirdparty/openxr/src/loader/loader_platform.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/loader/manifest_file.cpp b/thirdparty/openxr/src/loader/manifest_file.cpp
index 1b0ef07848..99f4e84104 100644
--- a/thirdparty/openxr/src/loader/manifest_file.cpp
+++ b/thirdparty/openxr/src/loader/manifest_file.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
@@ -27,13 +27,13 @@
#include <openxr/openxr.h>
#include <algorithm>
+#include <cstdlib>
+#include <cstdio>
#include <cstring>
#include <fstream>
#include <memory>
#include <sstream>
#include <stdexcept>
-#include <stdio.h>
-#include <stdlib.h>
#include <string>
#include <unordered_map>
#include <utility>
@@ -233,6 +233,12 @@ static void ReadDataFilesInSearchPaths(const std::string &override_env_var, cons
relative_home_path += relative_path;
CopyIncludedPaths(true, home, relative_home_path, search_path);
}
+#elif defined(XR_OS_ANDROID)
+ CopyIncludedPaths(true, "/product/etc", relative_path, search_path);
+ CopyIncludedPaths(true, "/odm/etc", relative_path, search_path);
+ CopyIncludedPaths(true, "/oem/etc", relative_path, search_path);
+ CopyIncludedPaths(true, "/vendor/etc", relative_path, search_path);
+ CopyIncludedPaths(true, "/system/etc", relative_path, search_path);
#else
(void)relative_path;
#endif
@@ -447,9 +453,8 @@ static void GetExtensionProperties(const std::vector<ExtensionListing> &extensio
if (it != props.end()) {
it->extensionVersion = std::max(it->extensionVersion, ext.extension_version);
} else {
- XrExtensionProperties prop = {};
+ XrExtensionProperties prop{};
prop.type = XR_TYPE_EXTENSION_PROPERTIES;
- prop.next = nullptr;
strncpy(prop.extensionName, ext.name.c_str(), XR_MAX_EXTENSION_NAME_SIZE - 1);
prop.extensionName[XR_MAX_EXTENSION_NAME_SIZE - 1] = '\0';
prop.extensionVersion = ext.extension_version;
diff --git a/thirdparty/openxr/src/loader/manifest_file.hpp b/thirdparty/openxr/src/loader/manifest_file.hpp
index de0aab65c2..46b842c663 100644
--- a/thirdparty/openxr/src/loader/manifest_file.hpp
+++ b/thirdparty/openxr/src/loader/manifest_file.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017 The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/loader/runtime_interface.cpp b/thirdparty/openxr/src/loader/runtime_interface.cpp
index 0f081ff9b2..d9ab86bb58 100644
--- a/thirdparty/openxr/src/loader/runtime_interface.cpp
+++ b/thirdparty/openxr/src/loader/runtime_interface.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
@@ -430,12 +430,10 @@ void RuntimeInterface::GetInstanceExtensionProperties(std::vector<XrExtensionPro
// Get the count from the runtime
rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, nullptr);
if (count_output > 0) {
- runtime_extension_properties.resize(count_output);
+ XrExtensionProperties example_properties{};
+ example_properties.type = XR_TYPE_EXTENSION_PROPERTIES;
+ runtime_extension_properties.resize(count_output, example_properties);
count = count_output;
- for (XrExtensionProperties& ext_prop : runtime_extension_properties) {
- ext_prop.type = XR_TYPE_EXTENSION_PROPERTIES;
- ext_prop.next = nullptr;
- }
rt_xrEnumerateInstanceExtensionProperties(nullptr, count, &count_output, runtime_extension_properties.data());
}
size_t ext_count = runtime_extension_properties.size();
diff --git a/thirdparty/openxr/src/loader/runtime_interface.hpp b/thirdparty/openxr/src/loader/runtime_interface.hpp
index fa53ee03f2..8d55ec674a 100644
--- a/thirdparty/openxr/src/loader/runtime_interface.hpp
+++ b/thirdparty/openxr/src/loader/runtime_interface.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/loader/xr_generated_loader.cpp b/thirdparty/openxr/src/loader/xr_generated_loader.cpp
index 2ce323e51f..e7767fd30a 100644
--- a/thirdparty/openxr/src/loader/xr_generated_loader.cpp
+++ b/thirdparty/openxr/src/loader/xr_generated_loader.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// SPDX-License-Identifier: Apache-2.0 OR MIT
@@ -6,7 +6,7 @@
// See loader_source_generator.py for modifications
// ************************************************************
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/loader/xr_generated_loader.hpp b/thirdparty/openxr/src/loader/xr_generated_loader.hpp
index 482cf1e83e..e28e35bbcf 100644
--- a/thirdparty/openxr/src/loader/xr_generated_loader.hpp
+++ b/thirdparty/openxr/src/loader/xr_generated_loader.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// SPDX-License-Identifier: Apache-2.0 OR MIT
@@ -6,7 +6,7 @@
// See loader_source_generator.py for modifications
// ************************************************************
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table.c b/thirdparty/openxr/src/xr_generated_dispatch_table.c
index 094f9fbbda..302bed31f5 100644
--- a/thirdparty/openxr/src/xr_generated_dispatch_table.c
+++ b/thirdparty/openxr/src/xr_generated_dispatch_table.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// SPDX-License-Identifier: Apache-2.0 OR MIT
@@ -6,7 +6,7 @@
// See utility_source_generator.py for modifications
// ************************************************************
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
@@ -27,6 +27,7 @@
// Author: Mark Young <marky@lunarg.com>
//
+#include <time.h>
#include "xr_generated_dispatch_table.h"
#include "xr_dependencies.h"
#include <openxr/openxr.h>
@@ -236,6 +237,12 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
(get_inst_proc_addr(instance, "xrUpdateSwapchainFB", (PFN_xrVoidFunction*)&table->UpdateSwapchainFB));
(get_inst_proc_addr(instance, "xrGetSwapchainStateFB", (PFN_xrVoidFunction*)&table->GetSwapchainStateFB));
+ // ---- XR_FB_body_tracking extension commands
+ (get_inst_proc_addr(instance, "xrCreateBodyTrackerFB", (PFN_xrVoidFunction*)&table->CreateBodyTrackerFB));
+ (get_inst_proc_addr(instance, "xrDestroyBodyTrackerFB", (PFN_xrVoidFunction*)&table->DestroyBodyTrackerFB));
+ (get_inst_proc_addr(instance, "xrLocateBodyJointsFB", (PFN_xrVoidFunction*)&table->LocateBodyJointsFB));
+ (get_inst_proc_addr(instance, "xrGetBodySkeletonFB", (PFN_xrVoidFunction*)&table->GetBodySkeletonFB));
+
// ---- XR_MSFT_scene_understanding extension commands
(get_inst_proc_addr(instance, "xrEnumerateSceneComputeFeaturesMSFT", (PFN_xrVoidFunction*)&table->EnumerateSceneComputeFeaturesMSFT));
(get_inst_proc_addr(instance, "xrCreateSceneObserverMSFT", (PFN_xrVoidFunction*)&table->CreateSceneObserverMSFT));
@@ -329,6 +336,11 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
// ---- XR_VARJO_view_offset extension commands
(get_inst_proc_addr(instance, "xrSetViewOffsetVARJO", (PFN_xrVoidFunction*)&table->SetViewOffsetVARJO));
+ // ---- XR_ML_compat extension commands
+#if defined(XR_USE_PLATFORM_ML)
+ (get_inst_proc_addr(instance, "xrCreateSpaceFromCoordinateFrameUIDML", (PFN_xrVoidFunction*)&table->CreateSpaceFromCoordinateFrameUIDML));
+#endif // defined(XR_USE_PLATFORM_ML)
+
// ---- XR_MSFT_spatial_anchor_persistence extension commands
(get_inst_proc_addr(instance, "xrCreateSpatialAnchorStoreConnectionMSFT", (PFN_xrVoidFunction*)&table->CreateSpatialAnchorStoreConnectionMSFT));
(get_inst_proc_addr(instance, "xrDestroySpatialAnchorStoreConnectionMSFT", (PFN_xrVoidFunction*)&table->DestroySpatialAnchorStoreConnectionMSFT));
@@ -354,6 +366,9 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
(get_inst_proc_addr(instance, "xrGetAudioInputDeviceGuidOculus", (PFN_xrVoidFunction*)&table->GetAudioInputDeviceGuidOculus));
#endif // defined(XR_USE_PLATFORM_WIN32)
+ // ---- XR_FB_spatial_entity_sharing extension commands
+ (get_inst_proc_addr(instance, "xrShareSpacesFB", (PFN_xrVoidFunction*)&table->ShareSpacesFB));
+
// ---- XR_FB_scene extension commands
(get_inst_proc_addr(instance, "xrGetSpaceBoundingBox2DFB", (PFN_xrVoidFunction*)&table->GetSpaceBoundingBox2DFB));
(get_inst_proc_addr(instance, "xrGetSpaceBoundingBox3DFB", (PFN_xrVoidFunction*)&table->GetSpaceBoundingBox3DFB));
@@ -364,24 +379,86 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
// ---- XR_ALMALENCE_digital_lens_control extension commands
(get_inst_proc_addr(instance, "xrSetDigitalLensControlALMALENCE", (PFN_xrVoidFunction*)&table->SetDigitalLensControlALMALENCE));
+ // ---- XR_FB_scene_capture extension commands
+ (get_inst_proc_addr(instance, "xrRequestSceneCaptureFB", (PFN_xrVoidFunction*)&table->RequestSceneCaptureFB));
+
// ---- XR_FB_spatial_entity_container extension commands
(get_inst_proc_addr(instance, "xrGetSpaceContainerFB", (PFN_xrVoidFunction*)&table->GetSpaceContainerFB));
+ // ---- XR_META_foveation_eye_tracked extension commands
+ (get_inst_proc_addr(instance, "xrGetFoveationEyeTrackedStateMETA", (PFN_xrVoidFunction*)&table->GetFoveationEyeTrackedStateMETA));
+
+ // ---- XR_FB_face_tracking extension commands
+ (get_inst_proc_addr(instance, "xrCreateFaceTrackerFB", (PFN_xrVoidFunction*)&table->CreateFaceTrackerFB));
+ (get_inst_proc_addr(instance, "xrDestroyFaceTrackerFB", (PFN_xrVoidFunction*)&table->DestroyFaceTrackerFB));
+ (get_inst_proc_addr(instance, "xrGetFaceExpressionWeightsFB", (PFN_xrVoidFunction*)&table->GetFaceExpressionWeightsFB));
+
+ // ---- XR_FB_eye_tracking_social extension commands
+ (get_inst_proc_addr(instance, "xrCreateEyeTrackerFB", (PFN_xrVoidFunction*)&table->CreateEyeTrackerFB));
+ (get_inst_proc_addr(instance, "xrDestroyEyeTrackerFB", (PFN_xrVoidFunction*)&table->DestroyEyeTrackerFB));
+ (get_inst_proc_addr(instance, "xrGetEyeGazesFB", (PFN_xrVoidFunction*)&table->GetEyeGazesFB));
+
// ---- XR_FB_passthrough_keyboard_hands extension commands
(get_inst_proc_addr(instance, "xrPassthroughLayerSetKeyboardHandsIntensityFB", (PFN_xrVoidFunction*)&table->PassthroughLayerSetKeyboardHandsIntensityFB));
+ // ---- XR_FB_haptic_pcm extension commands
+ (get_inst_proc_addr(instance, "xrGetDeviceSampleRateFB", (PFN_xrVoidFunction*)&table->GetDeviceSampleRateFB));
+
+ // ---- XR_META_virtual_keyboard extension commands
+ (get_inst_proc_addr(instance, "xrCreateVirtualKeyboardMETA", (PFN_xrVoidFunction*)&table->CreateVirtualKeyboardMETA));
+ (get_inst_proc_addr(instance, "xrDestroyVirtualKeyboardMETA", (PFN_xrVoidFunction*)&table->DestroyVirtualKeyboardMETA));
+ (get_inst_proc_addr(instance, "xrCreateVirtualKeyboardSpaceMETA", (PFN_xrVoidFunction*)&table->CreateVirtualKeyboardSpaceMETA));
+ (get_inst_proc_addr(instance, "xrSuggestVirtualKeyboardLocationMETA", (PFN_xrVoidFunction*)&table->SuggestVirtualKeyboardLocationMETA));
+ (get_inst_proc_addr(instance, "xrGetVirtualKeyboardScaleMETA", (PFN_xrVoidFunction*)&table->GetVirtualKeyboardScaleMETA));
+ (get_inst_proc_addr(instance, "xrSetVirtualKeyboardModelVisibilityMETA", (PFN_xrVoidFunction*)&table->SetVirtualKeyboardModelVisibilityMETA));
+ (get_inst_proc_addr(instance, "xrGetVirtualKeyboardModelAnimationStatesMETA", (PFN_xrVoidFunction*)&table->GetVirtualKeyboardModelAnimationStatesMETA));
+ (get_inst_proc_addr(instance, "xrGetVirtualKeyboardDirtyTexturesMETA", (PFN_xrVoidFunction*)&table->GetVirtualKeyboardDirtyTexturesMETA));
+ (get_inst_proc_addr(instance, "xrGetVirtualKeyboardTextureDataMETA", (PFN_xrVoidFunction*)&table->GetVirtualKeyboardTextureDataMETA));
+ (get_inst_proc_addr(instance, "xrSendVirtualKeyboardInputMETA", (PFN_xrVoidFunction*)&table->SendVirtualKeyboardInputMETA));
+ (get_inst_proc_addr(instance, "xrChangeVirtualKeyboardTextContextMETA", (PFN_xrVoidFunction*)&table->ChangeVirtualKeyboardTextContextMETA));
+
+ // ---- XR_OCULUS_external_camera extension commands
+ (get_inst_proc_addr(instance, "xrEnumerateExternalCamerasOCULUS", (PFN_xrVoidFunction*)&table->EnumerateExternalCamerasOCULUS));
+
// ---- XR_META_performance_metrics extension commands
(get_inst_proc_addr(instance, "xrEnumeratePerformanceMetricsCounterPathsMETA", (PFN_xrVoidFunction*)&table->EnumeratePerformanceMetricsCounterPathsMETA));
(get_inst_proc_addr(instance, "xrSetPerformanceMetricsStateMETA", (PFN_xrVoidFunction*)&table->SetPerformanceMetricsStateMETA));
(get_inst_proc_addr(instance, "xrGetPerformanceMetricsStateMETA", (PFN_xrVoidFunction*)&table->GetPerformanceMetricsStateMETA));
(get_inst_proc_addr(instance, "xrQueryPerformanceMetricsCounterMETA", (PFN_xrVoidFunction*)&table->QueryPerformanceMetricsCounterMETA));
+ // ---- XR_FB_spatial_entity_storage_batch extension commands
+ (get_inst_proc_addr(instance, "xrSaveSpaceListFB", (PFN_xrVoidFunction*)&table->SaveSpaceListFB));
+
+ // ---- XR_FB_spatial_entity_user extension commands
+ (get_inst_proc_addr(instance, "xrCreateSpaceUserFB", (PFN_xrVoidFunction*)&table->CreateSpaceUserFB));
+ (get_inst_proc_addr(instance, "xrGetSpaceUserIdFB", (PFN_xrVoidFunction*)&table->GetSpaceUserIdFB));
+ (get_inst_proc_addr(instance, "xrDestroySpaceUserFB", (PFN_xrVoidFunction*)&table->DestroySpaceUserFB));
+
+ // ---- XR_META_passthrough_color_lut extension commands
+ (get_inst_proc_addr(instance, "xrCreatePassthroughColorLutMETA", (PFN_xrVoidFunction*)&table->CreatePassthroughColorLutMETA));
+ (get_inst_proc_addr(instance, "xrDestroyPassthroughColorLutMETA", (PFN_xrVoidFunction*)&table->DestroyPassthroughColorLutMETA));
+ (get_inst_proc_addr(instance, "xrUpdatePassthroughColorLutMETA", (PFN_xrVoidFunction*)&table->UpdatePassthroughColorLutMETA));
+
+ // ---- XR_QCOM_tracking_optimization_settings extension commands
+ (get_inst_proc_addr(instance, "xrSetTrackingOptimizationSettingsHintQCOM", (PFN_xrVoidFunction*)&table->SetTrackingOptimizationSettingsHintQCOM));
+
// ---- XR_HTC_passthrough extension commands
(get_inst_proc_addr(instance, "xrCreatePassthroughHTC", (PFN_xrVoidFunction*)&table->CreatePassthroughHTC));
(get_inst_proc_addr(instance, "xrDestroyPassthroughHTC", (PFN_xrVoidFunction*)&table->DestroyPassthroughHTC));
// ---- XR_HTC_foveation extension commands
(get_inst_proc_addr(instance, "xrApplyFoveationHTC", (PFN_xrVoidFunction*)&table->ApplyFoveationHTC));
+
+ // ---- XR_MNDX_force_feedback_curl extension commands
+ (get_inst_proc_addr(instance, "xrApplyForceFeedbackCurlMNDX", (PFN_xrVoidFunction*)&table->ApplyForceFeedbackCurlMNDX));
+
+ // ---- XR_EXT_plane_detection extension commands
+ (get_inst_proc_addr(instance, "xrCreatePlaneDetectorEXT", (PFN_xrVoidFunction*)&table->CreatePlaneDetectorEXT));
+ (get_inst_proc_addr(instance, "xrDestroyPlaneDetectorEXT", (PFN_xrVoidFunction*)&table->DestroyPlaneDetectorEXT));
+ (get_inst_proc_addr(instance, "xrBeginPlaneDetectionEXT", (PFN_xrVoidFunction*)&table->BeginPlaneDetectionEXT));
+ (get_inst_proc_addr(instance, "xrGetPlaneDetectionStateEXT", (PFN_xrVoidFunction*)&table->GetPlaneDetectionStateEXT));
+ (get_inst_proc_addr(instance, "xrGetPlaneDetectionsEXT", (PFN_xrVoidFunction*)&table->GetPlaneDetectionsEXT));
+ (get_inst_proc_addr(instance, "xrGetPlanePolygonBufferEXT", (PFN_xrVoidFunction*)&table->GetPlanePolygonBufferEXT));
}
diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table.h b/thirdparty/openxr/src/xr_generated_dispatch_table.h
index 93d07a149e..b6e17f98d4 100644
--- a/thirdparty/openxr/src/xr_generated_dispatch_table.h
+++ b/thirdparty/openxr/src/xr_generated_dispatch_table.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
// SPDX-License-Identifier: Apache-2.0 OR MIT
@@ -6,7 +6,7 @@
// See utility_source_generator.py for modifications
// ************************************************************
-// Copyright (c) 2017-2022, The Khronos Group Inc.
+// Copyright (c) 2017-2023, The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
@@ -239,6 +239,12 @@ struct XrGeneratedDispatchTable {
PFN_xrUpdateSwapchainFB UpdateSwapchainFB;
PFN_xrGetSwapchainStateFB GetSwapchainStateFB;
+ // ---- XR_FB_body_tracking extension commands
+ PFN_xrCreateBodyTrackerFB CreateBodyTrackerFB;
+ PFN_xrDestroyBodyTrackerFB DestroyBodyTrackerFB;
+ PFN_xrLocateBodyJointsFB LocateBodyJointsFB;
+ PFN_xrGetBodySkeletonFB GetBodySkeletonFB;
+
// ---- XR_MSFT_scene_understanding extension commands
PFN_xrEnumerateSceneComputeFeaturesMSFT EnumerateSceneComputeFeaturesMSFT;
PFN_xrCreateSceneObserverMSFT CreateSceneObserverMSFT;
@@ -332,6 +338,11 @@ struct XrGeneratedDispatchTable {
// ---- XR_VARJO_view_offset extension commands
PFN_xrSetViewOffsetVARJO SetViewOffsetVARJO;
+ // ---- XR_ML_compat extension commands
+#if defined(XR_USE_PLATFORM_ML)
+ PFN_xrCreateSpaceFromCoordinateFrameUIDML CreateSpaceFromCoordinateFrameUIDML;
+#endif // defined(XR_USE_PLATFORM_ML)
+
// ---- XR_MSFT_spatial_anchor_persistence extension commands
PFN_xrCreateSpatialAnchorStoreConnectionMSFT CreateSpatialAnchorStoreConnectionMSFT;
PFN_xrDestroySpatialAnchorStoreConnectionMSFT DestroySpatialAnchorStoreConnectionMSFT;
@@ -357,6 +368,9 @@ struct XrGeneratedDispatchTable {
PFN_xrGetAudioInputDeviceGuidOculus GetAudioInputDeviceGuidOculus;
#endif // defined(XR_USE_PLATFORM_WIN32)
+ // ---- XR_FB_spatial_entity_sharing extension commands
+ PFN_xrShareSpacesFB ShareSpacesFB;
+
// ---- XR_FB_scene extension commands
PFN_xrGetSpaceBoundingBox2DFB GetSpaceBoundingBox2DFB;
PFN_xrGetSpaceBoundingBox3DFB GetSpaceBoundingBox3DFB;
@@ -367,24 +381,86 @@ struct XrGeneratedDispatchTable {
// ---- XR_ALMALENCE_digital_lens_control extension commands
PFN_xrSetDigitalLensControlALMALENCE SetDigitalLensControlALMALENCE;
+ // ---- XR_FB_scene_capture extension commands
+ PFN_xrRequestSceneCaptureFB RequestSceneCaptureFB;
+
// ---- XR_FB_spatial_entity_container extension commands
PFN_xrGetSpaceContainerFB GetSpaceContainerFB;
+ // ---- XR_META_foveation_eye_tracked extension commands
+ PFN_xrGetFoveationEyeTrackedStateMETA GetFoveationEyeTrackedStateMETA;
+
+ // ---- XR_FB_face_tracking extension commands
+ PFN_xrCreateFaceTrackerFB CreateFaceTrackerFB;
+ PFN_xrDestroyFaceTrackerFB DestroyFaceTrackerFB;
+ PFN_xrGetFaceExpressionWeightsFB GetFaceExpressionWeightsFB;
+
+ // ---- XR_FB_eye_tracking_social extension commands
+ PFN_xrCreateEyeTrackerFB CreateEyeTrackerFB;
+ PFN_xrDestroyEyeTrackerFB DestroyEyeTrackerFB;
+ PFN_xrGetEyeGazesFB GetEyeGazesFB;
+
// ---- XR_FB_passthrough_keyboard_hands extension commands
PFN_xrPassthroughLayerSetKeyboardHandsIntensityFB PassthroughLayerSetKeyboardHandsIntensityFB;
+ // ---- XR_FB_haptic_pcm extension commands
+ PFN_xrGetDeviceSampleRateFB GetDeviceSampleRateFB;
+
+ // ---- XR_META_virtual_keyboard extension commands
+ PFN_xrCreateVirtualKeyboardMETA CreateVirtualKeyboardMETA;
+ PFN_xrDestroyVirtualKeyboardMETA DestroyVirtualKeyboardMETA;
+ PFN_xrCreateVirtualKeyboardSpaceMETA CreateVirtualKeyboardSpaceMETA;
+ PFN_xrSuggestVirtualKeyboardLocationMETA SuggestVirtualKeyboardLocationMETA;
+ PFN_xrGetVirtualKeyboardScaleMETA GetVirtualKeyboardScaleMETA;
+ PFN_xrSetVirtualKeyboardModelVisibilityMETA SetVirtualKeyboardModelVisibilityMETA;
+ PFN_xrGetVirtualKeyboardModelAnimationStatesMETA GetVirtualKeyboardModelAnimationStatesMETA;
+ PFN_xrGetVirtualKeyboardDirtyTexturesMETA GetVirtualKeyboardDirtyTexturesMETA;
+ PFN_xrGetVirtualKeyboardTextureDataMETA GetVirtualKeyboardTextureDataMETA;
+ PFN_xrSendVirtualKeyboardInputMETA SendVirtualKeyboardInputMETA;
+ PFN_xrChangeVirtualKeyboardTextContextMETA ChangeVirtualKeyboardTextContextMETA;
+
+ // ---- XR_OCULUS_external_camera extension commands
+ PFN_xrEnumerateExternalCamerasOCULUS EnumerateExternalCamerasOCULUS;
+
// ---- XR_META_performance_metrics extension commands
PFN_xrEnumeratePerformanceMetricsCounterPathsMETA EnumeratePerformanceMetricsCounterPathsMETA;
PFN_xrSetPerformanceMetricsStateMETA SetPerformanceMetricsStateMETA;
PFN_xrGetPerformanceMetricsStateMETA GetPerformanceMetricsStateMETA;
PFN_xrQueryPerformanceMetricsCounterMETA QueryPerformanceMetricsCounterMETA;
+ // ---- XR_FB_spatial_entity_storage_batch extension commands
+ PFN_xrSaveSpaceListFB SaveSpaceListFB;
+
+ // ---- XR_FB_spatial_entity_user extension commands
+ PFN_xrCreateSpaceUserFB CreateSpaceUserFB;
+ PFN_xrGetSpaceUserIdFB GetSpaceUserIdFB;
+ PFN_xrDestroySpaceUserFB DestroySpaceUserFB;
+
+ // ---- XR_META_passthrough_color_lut extension commands
+ PFN_xrCreatePassthroughColorLutMETA CreatePassthroughColorLutMETA;
+ PFN_xrDestroyPassthroughColorLutMETA DestroyPassthroughColorLutMETA;
+ PFN_xrUpdatePassthroughColorLutMETA UpdatePassthroughColorLutMETA;
+
+ // ---- XR_QCOM_tracking_optimization_settings extension commands
+ PFN_xrSetTrackingOptimizationSettingsHintQCOM SetTrackingOptimizationSettingsHintQCOM;
+
// ---- XR_HTC_passthrough extension commands
PFN_xrCreatePassthroughHTC CreatePassthroughHTC;
PFN_xrDestroyPassthroughHTC DestroyPassthroughHTC;
// ---- XR_HTC_foveation extension commands
PFN_xrApplyFoveationHTC ApplyFoveationHTC;
+
+ // ---- XR_MNDX_force_feedback_curl extension commands
+ PFN_xrApplyForceFeedbackCurlMNDX ApplyForceFeedbackCurlMNDX;
+
+ // ---- XR_EXT_plane_detection extension commands
+ PFN_xrCreatePlaneDetectorEXT CreatePlaneDetectorEXT;
+ PFN_xrDestroyPlaneDetectorEXT DestroyPlaneDetectorEXT;
+ PFN_xrBeginPlaneDetectionEXT BeginPlaneDetectionEXT;
+ PFN_xrGetPlaneDetectionStateEXT GetPlaneDetectionStateEXT;
+ PFN_xrGetPlaneDetectionsEXT GetPlaneDetectionsEXT;
+ PFN_xrGetPlanePolygonBufferEXT GetPlanePolygonBufferEXT;
};